You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by ld...@apache.org on 2020/10/21 08:43:00 UTC

[plc4x] branch feature/socketcan updated (30d0802 -> 94fb0cb)

This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a change to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git.


 discard 30d0802  Combine socketcan and canopen frames together through dedicated IO classes.
 discard 2b3f594  Working on SDO upload (read) operation coordination.
 discard 4cb98f9  - Tweaked the handling of arrays in dataIo serializers
 discard 0b699e8  CANopen microstone - PDO & subscriptions.
 discard 93d5d70  CANopen milestone - support for segmentet SDO upload (read) requests.
 discard 5d1e10c  Test of wireshark frames.
 discard 695c0b6  CANopen adjustments.
 discard 8cb18bf  Fix socketcan mspec.
 discard 71301c9  More tests of canopen payload.
 discard c7c28fd  Fix socketcan mspec errors.
 discard ea5b953  Update service list for canopen.
 discard 5196344  Sketch of support for SDO operations.
 discard ccdeb58  Fix socketcan transport.
 discard 846e03c  More work on socketcan transport.
 discard d60b64a  Focus on payloads and not frames.
 discard 4c3b4c1  Split different flavours of CAN formats.
 discard b304744  More definitions of various frame formats.
 discard 03ad153  Step back to virtual fields in can transport.
 discard 78fe2ee  Tests for socketcan frames + manual parsing based on ReadBuffer api.
 discard fe7b9c1  Scratch of socketcan transport and can frame definitions.
 discard c825f97  - removed the toPlcValue method from the Message interface (It was actually only used in one place of the experimental BACnetIp driver)
 discard b60bba2  - Updated my company
     add 1b9ee8d  - Updated my company
     add dc60b42  - removed the toPlcValue method from the Message interface (It was actually only used in one place of the experimental BACnetIp driver)
     add 1f51a69  added Apache Con 2020 Videos
     add 040ed71  Update blogs-videos-and-slides.adoc
     add 8df8207  Reverted Commit 90fc914ec3e72a91af514742fb6dc85a34e99c9f
     add af00614  Removed Class Lookup in new PlcValues.of Function Add getPlcDataType to Plcfield Interface to allow generalizing the fieldHandler classes. Add getBooleanArray() to WORD type PlcValue classes. Add fix for staticSerializer when writing lists.
     add 5c95e44  Add checking for malformed write requests Modbus When writing arrays if the number of values didn't match the number of addresses a malformed packet was sent. Additional checks have been adedd. Error checking the response for writes has been added so that it reports error codes.
     add 58827f9  Merge pull request #192 from hutcheb/RefactorFieldHandler
     add 9af17c2  Add an OPC UA Server
     add f4f9bdf  Added requirement to set the security dir Otherwise it will create a security dir in the working dir.
     add 2682f46  Add OPC UA Server to plc4x poms Added a with-opcua-server option. Used the projects dependancy versions. Split out the Plc4x comms logic into it's one files so they aren't mixed up in the OPC UA server logic. Added an outline for the OPC UA Server README file
     add 5a24d98  Played around with specifying the data type. We should be able to use a base data type in opc ua as we do all the conversions already in Plcx. Handling of Arrays could be better by creating a node for each item in the array.
     add f65c2aa  Updated the handling of the passwords Passwords are entered on startup if they don't exist and then saved to the config file. They still need to be encrypted.
     add 9de9e7c  Update README.adoc
     add cc303a2  Merge branch 'Feature/opcua-server' of https://github.com/hutcheb/plc4x into Feature/opcua-server
     add 25df18f  Bumped the OPCUA driver version of Milo. Also went ot he latest stable build 5.1 instead of 5.2-snapshot for the opcua server.
     add b6aa1d0  Merge pull request #194 from hutcheb/Feature/opcua-server
     add 2788dc4  add logo record evolution
     add e3a87b7  Added Record Evolution
     add a3ae0a1  Delete logo_record_evolution_single_dark_bgfont.svg
     add 9d272f8  Add record evolution logo as png
     add 7d5eb73  changed record evolution logo to png
     add bf6ac83  Brief support for AMS/ADS discovery packets.
     new 76e5724  Scratch of socketcan transport and can frame definitions.
     new 7447def  Tests for socketcan frames + manual parsing based on ReadBuffer api.
     new 16dbb63  Step back to virtual fields in can transport.
     new e974615  More definitions of various frame formats.
     new 36ed9df  Split different flavours of CAN formats.
     new 9c612bc  Focus on payloads and not frames.
     new b2b0bd3  More work on socketcan transport.
     new 8c3122e  Fix socketcan transport.
     new d5ff622  Sketch of support for SDO operations.
     new d31f30d  Update service list for canopen.
     new d936e28  Fix socketcan mspec errors.
     new 015724e  More tests of canopen payload.
     new 43d1303  Fix socketcan mspec.
     new 59c7cc6  CANopen adjustments.
     new ce8b123  Test of wireshark frames.
     new a1ec214  CANopen milestone - support for segmentet SDO upload (read) requests.
     new 6aa220e  CANopen microstone - PDO & subscriptions.
     new d7a6570  Working on SDO upload (read) operation coordination.
     new fdd6347  Combine socketcan and canopen frames together through dedicated IO classes.
     new 13ebb2a  Support for NMT subscriptions.
     new 94fb0cb  Support for NMT and HEARTBEAT subscriptions.

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (30d0802)
            \
             N -- N -- N   refs/heads/feature/socketcan (94fb0cb)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 21 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/plc4x/java/api/model/PlcField.java  |   16 +
 .../org/apache/plc4x/java/api/value/PlcBOOL.java   |    5 -
 .../org/apache/plc4x/java/api/value/PlcBYTE.java   |   16 +-
 .../org/apache/plc4x/java/api/value/PlcCHAR.java   |    5 -
 .../org/apache/plc4x/java/api/value/PlcDINT.java   |    5 -
 .../org/apache/plc4x/java/api/value/PlcDWORD.java  |   16 +-
 .../org/apache/plc4x/java/api/value/PlcINT.java    |    5 -
 .../org/apache/plc4x/java/api/value/PlcLINT.java   |    5 -
 .../org/apache/plc4x/java/api/value/PlcLREAL.java  |    5 -
 .../org/apache/plc4x/java/api/value/PlcLWORD.java  |   16 +-
 .../org/apache/plc4x/java/api/value/PlcREAL.java   |    5 -
 .../org/apache/plc4x/java/api/value/PlcSINT.java   |    5 -
 .../org/apache/plc4x/java/api/value/PlcString.java |    6 +
 .../org/apache/plc4x/java/api/value/PlcUDINT.java  |    5 -
 .../org/apache/plc4x/java/api/value/PlcUINT.java   |    5 -
 .../org/apache/plc4x/java/api/value/PlcULINT.java  |    5 -
 .../org/apache/plc4x/java/api/value/PlcUSINT.java  |    5 -
 .../org/apache/plc4x/java/api/value/PlcValues.java |  144 +-
 .../org/apache/plc4x/java/api/value/PlcWCHAR.java  |    5 -
 .../org/apache/plc4x/java/api/value/PlcWORD.java   |   18 +-
 .../apache/plc4x/java/abeth/field/AbEthField.java  |    5 +
 plc4j/drivers/ads/pom.xml                          |   14 +-
 .../plc4x/java/ads/field/AdsFieldHandler.java      | 1046 ----------
 .../plc4x/java/ads/field/DirectAdsField.java       |    5 +
 .../plc4x/java/ads/field/SymbolicAdsField.java     |    5 +
 ....java => AdsDiscoverySerializerParserTest.java} |    6 +-
 .../testsuite/AdsDiscoverySerializerTest.xml       |  377 ++++
 .../plc4x/java/eip/readwrite/field/EipField.java   |    5 +
 .../java/eip/readwrite/field/EipFieldHandler.java  |  185 +-
 .../readwrite/field/FirmataFieldAnalog.java        |    5 +
 .../readwrite/field/FirmataFieldDigital.java       |    5 +
 .../readwrite/field/FirmataFieldHandler.java       |   60 -
 .../plc4x/java/knxnetip/field/KnxNetIpField.java   |    5 +
 .../java/knxnetip/field/KnxNetIpFieldHandler.java  |   67 -
 .../apache/plc4x/java/mock/field/MockField.java    |    5 +
 .../plc4x/java/modbus/field/ModbusField.java       |    5 +
 .../java/modbus/field/ModbusFieldHandler.java      |   64 -
 .../java/modbus/protocol/ModbusProtocolLogic.java  |   85 +-
 plc4j/drivers/opcua/pom.xml                        |    4 -
 .../opcua/connection/OpcuaTcpPlcConnection.java    |    2 +-
 .../plc4x/java/opcua/protocol/OpcuaField.java      |    5 +
 .../java/opcua/protocol/OpcuaPlcFieldHandler.java  |   74 -
 .../plc4x/java/opcua/OpcuaPlcDriverTest.java       |    2 +-
 .../plc4x/java/s7/readwrite/field/S7Field.java     |    4 +
 .../java/s7/readwrite/field/S7PlcFieldHandler.java |  169 --
 .../plc4x/java/simulated/field/SimulatedField.java |    4 +
 .../simulated/field/SimulatedFieldHandler.java     |  121 +-
 .../simulated/field/SimularedFieldHandlerTest.java |   19 +-
 .../spi/connection/DefaultPlcFieldHandler.java     | 1991 +++++++++++++++++++-
 .../java/opm/PlcEntityManagerComplexTest.java      |    5 +-
 pom.xml                                            |   34 +-
 ...{ADSProtocol.java => ADSDiscoveryProtocol.java} |    9 +-
 ...e.plc4x.plugins.codegenerator.protocol.Protocol |    3 +-
 .../resources/protocols/ads/ads-discovery.mspec    |   89 +-
 sandbox/opcua-server/README.adoc                   |   98 +
 sandbox/opcua-server/pom.xml                       |  216 +++
 .../java/opcuaserver/AttributeLoggingFilter.java   |   73 +
 .../plc4x/java/opcuaserver/Configuration.java      |  126 ++
 .../java/opcuaserver/DeviceConfiguration.java      |   35 +-
 .../plc4x/java/opcuaserver/KeyStoreLoader.java     |  163 ++
 .../apache/plc4x/java/opcuaserver/OPCUAServer.java |  334 ++++
 .../java/opcuaserver/RestrictedAccessFilter.java   |   55 +
 .../org/apache/plc4x/java/opcuaserver/Tag.java     |   27 +-
 .../opcuaserver/backend/Plc4xCommunication.java    |  177 ++
 .../java/opcuaserver/backend/Plc4xNamespace.java   |  215 +++
 sandbox/opcua-server/src/main/resources/config.yml |   43 +
 .../src/main/resources/logback.xml                 |    4 +-
 .../org/apache/plc4x/java/opcuaserver/AppTest.java |   21 +-
 sandbox/pom.xml                                    |   10 +-
 sandbox/test-java-can-driver/pom.xml               |   13 +
 .../api/segmentation/accumulator/ByteStorage.java  |   18 +
 .../apache/plc4x/java/can/field/CANOpenField.java  |    4 +
 ...penNMTField.java => CANOpenHeartbeatField.java} |   18 +-
 .../plc4x/java/can/field/CANOpenNMTField.java      |   10 +-
 .../plc4x/java/can/field/CANOpenPDOField.java      |    8 +-
 .../java/can/field/CANOpenSubscriptionField.java   |   11 +
 .../java/can/protocol/CANOpenProtocolLogic.java    |   85 +-
 .../can/protocol/CANOpenSubscriptionHandle.java    |   11 +-
 .../plc4x/java/can/field/CANOpenNMTFieldTest.java  |    9 +-
 ...nPDOFieldTest.java => CANOpenSDOFieldTest.java} |   11 +-
 .../resources/testsuite/CANOpenDriverSDOIT.xml     |  324 ++++
 src/site/asciidoc/users/adopters.adoc              |    3 +
 .../asciidoc/users/blogs-videos-and-slides.adoc    |    3 +
 .../logo_record_evolution_single_dark_bgfont.png   |  Bin 0 -> 10498 bytes
 84 files changed, 4827 insertions(+), 2079 deletions(-)
 copy plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/{AdsSerializerParserTest.java => AdsDiscoverySerializerParserTest.java} (82%)
 create mode 100644 plc4j/drivers/ads/src/test/resources/testsuite/AdsDiscoverySerializerTest.xml
 copy protocols/ads/src/main/java/org/apache/plc4x/protocol/ads/{ADSProtocol.java => ADSDiscoveryProtocol.java} (84%)
 create mode 100644 sandbox/opcua-server/README.adoc
 create mode 100644 sandbox/opcua-server/pom.xml
 create mode 100644 sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/AttributeLoggingFilter.java
 create mode 100644 sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/Configuration.java
 copy plc4j/api/src/test/java/org/apache/plc4x/java/api/messages/PlcSubscriptionResponseTest.java => sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/DeviceConfiguration.java (62%)
 create mode 100644 sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/KeyStoreLoader.java
 create mode 100644 sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/OPCUAServer.java
 create mode 100644 sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/RestrictedAccessFilter.java
 copy plc4j/tools/scraper/src/main/java/org/apache/plc4x/java/scraper/config/JobConfiguration.java => sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/Tag.java (70%)
 create mode 100644 sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xCommunication.java
 create mode 100644 sandbox/opcua-server/src/main/java/org/apache/plc4x/java/opcuaserver/backend/Plc4xNamespace.java
 create mode 100644 sandbox/opcua-server/src/main/resources/config.yml
 copy sandbox/{hello-influx-data-collection => opcua-server}/src/main/resources/logback.xml (97%)
 copy plc4j/api/src/test/java/org/apache/plc4x/java/api/exceptions/PlcFieldRangeExceptionTest.java => sandbox/opcua-server/src/test/java/org/apache/plc4x/java/opcuaserver/AppTest.java (77%)
 copy sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/{CANOpenNMTField.java => CANOpenHeartbeatField.java} (73%)
 create mode 100644 sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenSubscriptionField.java
 copy sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/{CANOpenPDOFieldTest.java => CANOpenSDOFieldTest.java} (66%)
 create mode 100644 sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenDriverSDOIT.xml
 create mode 100644 src/site/resources/images/users/companies/logo_record_evolution_single_dark_bgfont.png


[plc4x] 04/21: More definitions of various frame formats.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit e97461569fd31bd09fbb08ed3dfdbbba0c7b7ed5
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Wed Aug 26 23:49:33 2020 +0200

    More definitions of various frame formats.
---
 .../can/src/main/resources/protocols/can/can.mspec | 67 ++++++++++++++++++----
 .../apache/plc4x/java/can/helper/HeaderParser.java |  4 +-
 .../apache/plc4x/java/can/ManualParserTest.java    |  3 +
 .../apache/plc4x/java/can/SocketCAN20ATest.java    | 30 ++++++++++
 ...tCANTestSuite.xml => SocketCAN20ATestSuite.xml} | 45 ++++++++++-----
 ...tCANTestSuite.xml => SocketCAN20BTestSuite.xml} | 26 ++-------
 .../resources/testsuite/SocketCANTestSuite.xml     | 42 ++++++++++----
 7 files changed, 156 insertions(+), 61 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/can.mspec b/protocols/can/src/main/resources/protocols/can/can.mspec
index 16408fd..147492b 100644
--- a/protocols/can/src/main/resources/protocols/can/can.mspec
+++ b/protocols/can/src/main/resources/protocols/can/can.mspec
@@ -52,10 +52,35 @@ struct canfd_frame {
 };
 */
 
+[type 'BrokenSocketCANFrame'
+    [discriminator bit 'extended']
+    [simple bit 'remote']
+    [simple bit 'error']
+    [simple bit 'extended']
+    [typeSwitch 'extended'
+        ['true' ExtendedSocketCANFrame
+            [simple uint 29 'identifier']
+        ]
+
+        ['false' StandardSocketCANFrame
+            [const  uint 18 '0x0']
+            [simple uint 11 'identifier']
+        ]
+    ]
+    [implicit uint 8 'size' 'COUNT(data)']
+    [reserved uint 8 '0x0'] // padding
+    [reserved uint 8 '0x0'] // reserved / padding
+    [reserved uint 8 '0x0'] // reserved / padding
+    [array int 8 'data' COUNT 'size']
+]
+
 [type 'SocketCANFrame'
     [simple int 32 'rawId']
+    [virtual int 32 'identifier'
+        'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.readIdentifier", rawId)'
+    ]
     [virtual bit 'extended'
-        'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isRemote", rawId)'
+        'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isExtended", rawId)'
     ]
     [virtual bit 'remote'
         'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isRemote", rawId)'
@@ -64,28 +89,48 @@ struct canfd_frame {
         'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isError", rawId)'
     ]
     [implicit uint 8 'size' 'COUNT(data)']
-//    [typeSwitch 'extended'
-//        ['true' ExtendedOtherSocketCanFrame
-//            [simple uint 8 'flags']
-//        ]
-//        ['false' ExtendedOtherSocketCanFrame
-            [reserved uint 8 '0x0']
-//        ]
-//    ]
     [reserved uint 8 '0x0'] //flags
     [reserved uint 8 '0x0'] // padding 1
     [reserved uint 8 '0x0'] // padding 2
     [array int 8 'data' COUNT 'size']
 ]
 
+[type 'SocketCAN20AFrame'
+    [simple int 16 'identifier']
+    [reserved int 8 '0x0'] // filling gap used by extended frame
+    [simple bit 'extended']
+    [simple bit 'remote']
+    [simple bit 'error']
+    [reserved int 5 '0x0']  // filling gap used by extended frame
+    [implicit uint 8 'size' 'COUNT(data)']
+    [reserved uint 8 '0x0'] // in case of fd frame these are flags
+    [reserved uint 8 '0x0'] // padding 1
+    [reserved uint 8 '0x0'] // padding 2
+    [array int 8 'data' COUNT 'size']
+]
+
 [type 'SimplifiedSocketCANFrame'
     [simple bit 'extended']
     [simple bit 'remote']
     [simple bit 'error']
     [simple uint 29 'identifier']
-    [implicit uint 8 'length' 'COUNT(data)']
+    //implicit uint 8 'size' 'COUNT(data)'
     [reserved uint 8 '0x0'] // flags
     [reserved uint 8 '0x0'] // padding
     [reserved uint 8 '0x0'] // padding
-    [array int 8 'data' COUNT 'length']
+    //array int 8 'data' COUNT 'size'
+]
+
+[enum 'CanOpenNMTCommand'
+    ['0x01', START_DEVICE]
+    ['0x02', STOP_DEVICE]
+    ['0x80', PRE_START]
+    ['0x81', RESET_DEVICE]
+    ['0x82', RESET_COMMUNICATION]
 ]
+
+[dataIo 'CANOpenFrame' [uint 4 'function', uint 7 nodeId, int 8 'data']
+    [discriminator uint 4 'afunction']
+    [typeSwitch 'afunction'
+    ]
+]
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
index 3ce7f0f..c0363f6 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
@@ -2,7 +2,6 @@ package org.apache.plc4x.java.can.helper;
 
 import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.spi.generation.ParseException;
-import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
 public class HeaderParser {
@@ -17,8 +16,7 @@ public class HeaderParser {
 
     public static final int EXTENDED_FORMAT_IDENTIFIER_MASK = 0x1fffffff;
 
-    public static int readIdentifier(ReadBuffer buffer) throws ParseException {
-        int identifier = buffer.readInt(32);
+    public static int readIdentifier(int identifier) {
         if ((identifier & EXTENDED_FORMAT_IDENTIFIER_MASK) == 0) {
             return identifier & STANDARD_FORMAT_IDENTIFIER_MASK;
         }
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/ManualParserTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/ManualParserTest.java
index 11c6ca6..c055513 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/ManualParserTest.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/ManualParserTest.java
@@ -18,6 +18,9 @@ public class ManualParserTest {
     // cansend 5A1#11.2233.44556677.88
     String STANDARD = "a1050000080000001122334455667788";
 
+    // cansend 5A1#R
+    String STANDARD_REPLY = "a1050040000000000000000000000000";
+
     // cansend 1E6EC676#05.05.1F.26.C3
     String EXTENDED = "76c66e9e0500000005051f26c3000000";
 
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/SocketCAN20ATest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/SocketCAN20ATest.java
new file mode 100644
index 0000000..8d86d97
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/SocketCAN20ATest.java
@@ -0,0 +1,30 @@
+/*
+  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.can;
+
+import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
+
+public class SocketCAN20ATest extends ParserSerializerTestsuiteRunner {
+
+    public SocketCAN20ATest() {
+        super("/testsuite/SocketCAN20ATestSuite.xml");
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20ATestSuite.xml
similarity index 53%
copy from sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
copy to sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20ATestSuite.xml
index 774563b..60140de 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20ATestSuite.xml
@@ -19,35 +19,50 @@
   -->
 <test:testsuite xmlns:test="https://plc4x.apache.org/schemas/parser-serializer-testsuite.xsd">
 
-  <name>CAN Standard Format Frame</name>
+  <name>Tests of socketcan/CAN 2.0A frames</name>
 
   <testcase>
-    <name>Extended frame 1E6EC676#05.05.1F.26.C3</name>
-    <raw>76c66e9e0500000005051f26c3000000</raw>
-    <root-type>SocketCANFrame</root-type>
+    <name>Standard frame with 5 byte payload: 5A1#11.2233.44556677.88</name>
+    <raw>a1050000080000001122334455667788</raw>
+    <root-type>SocketCAN20AFrame</root-type>
     <xml>
-      <SocketCANFDFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
-        <identifier>510576246</identifier>
-        <extended>true</extended>
+      <SocketCAN20AFrame className="org.apache.plc4x.java.can.readwrite.SocketCAN20AFrame">
+        <identifier>1441</identifier>
+        <extended>false</extended>
         <remote>false</remote>
-        <error>true</error>
-        <data></data>
-      </SocketCANFDFrame>
+        <error>false</error>
+        <data>ESIzRFVmd4g=</data>
+      </SocketCAN20AFrame>
     </xml>
   </testcase>
 
   <testcase>
-    <name>Standard frame 5A1#11.2233.44556677.88</name>
-    <raw>a1050000080000001122334455667788</raw>
-    <root-type>SocketCANFrame</root-type>
+    <name>Standard frame with no payload: 5A1#</name>
+    <raw>a105000000000000</raw>
+    <root-type>SocketCAN20AFrame</root-type>
     <xml>
-      <ScoketCANSFFrame className="org.apache.plc4x.java.can.readwrite.ScoketCANFrame">
+      <SocketCAN20AFrame className="org.apache.plc4x.java.can.readwrite.SocketCAN20AFrame">
         <identifier>1441</identifier>
         <extended>false</extended>
         <remote>false</remote>
         <error>false</error>
         <data></data>
-      </ScoketCANSFFrame>
+      </SocketCAN20AFrame>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>Remote transmission request: 5A1#R</name>
+    <raw>a105004000000000</raw>
+    <root-type>SocketCAN20AFrame</root-type>
+    <xml>
+      <SocketCAN20AFrame className="org.apache.plc4x.java.can.readwrite.SocketCAN20AFrame">
+        <identifier>1441</identifier>
+        <extended>false</extended>
+        <remote>true</remote>
+        <error>false</error>
+        <data></data>
+      </SocketCAN20AFrame>
     </xml>
   </testcase>
 
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
similarity index 63%
copy from sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
copy to sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
index 774563b..e8f08f4 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
@@ -19,35 +19,21 @@
   -->
 <test:testsuite xmlns:test="https://plc4x.apache.org/schemas/parser-serializer-testsuite.xsd">
 
-  <name>CAN Standard Format Frame</name>
+  <name>Tests of socketcan/CAN 2.0B frames</name>
 
   <testcase>
     <name>Extended frame 1E6EC676#05.05.1F.26.C3</name>
-    <raw>76c66e9e0500000005051f26c3000000</raw>
+    <raw>76c66e9e0500000005051f26c3</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
-      <SocketCANFDFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+        <rawId>-1636907402</rawId>
+        <data>BQUfJsM=</data>
         <identifier>510576246</identifier>
         <extended>true</extended>
         <remote>false</remote>
-        <error>true</error>
-        <data></data>
-      </SocketCANFDFrame>
-    </xml>
-  </testcase>
-
-  <testcase>
-    <name>Standard frame 5A1#11.2233.44556677.88</name>
-    <raw>a1050000080000001122334455667788</raw>
-    <root-type>SocketCANFrame</root-type>
-    <xml>
-      <ScoketCANSFFrame className="org.apache.plc4x.java.can.readwrite.ScoketCANFrame">
-        <identifier>1441</identifier>
-        <extended>false</extended>
-        <remote>false</remote>
         <error>false</error>
-        <data></data>
-      </ScoketCANSFFrame>
+      </SocketCANFrame>
     </xml>
   </testcase>
 
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
index 774563b..f2518c0 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
@@ -22,32 +22,50 @@
   <name>CAN Standard Format Frame</name>
 
   <testcase>
-    <name>Extended frame 1E6EC676#05.05.1F.26.C3</name>
-    <raw>76c66e9e0500000005051f26c3000000</raw>
+    <name>Standard frame with 5 byte payload: 5A1#11.2233.44556677.88</name>
+    <raw>a1050000080000001122334455667788</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
-      <SocketCANFDFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
-        <identifier>510576246</identifier>
-        <extended>true</extended>
+      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+        <rawId>1441</rawId>
+        <data>ESIzRFVmd4g=</data>
+        <identifier>1441</identifier>
+        <extended>false</extended>
         <remote>false</remote>
-        <error>true</error>
-        <data></data>
-      </SocketCANFDFrame>
+        <error>false</error>
+      </SocketCANFrame>
     </xml>
   </testcase>
 
   <testcase>
-    <name>Standard frame 5A1#11.2233.44556677.88</name>
-    <raw>a1050000080000001122334455667788</raw>
+    <name>Standard frame with no payload: 5A1#</name>
+    <raw>a105000000000000</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
-      <ScoketCANSFFrame className="org.apache.plc4x.java.can.readwrite.ScoketCANFrame">
+      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+        <rawId>1441</rawId>
+        <data></data>
         <identifier>1441</identifier>
         <extended>false</extended>
         <remote>false</remote>
         <error>false</error>
+      </SocketCANFrame>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>Remote transmission request: 5A1#R</name>
+    <raw>a105004000000000</raw>
+    <root-type>SocketCANFrame</root-type>
+    <xml>
+      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+        <rawId>1073743265</rawId>
         <data></data>
-      </ScoketCANSFFrame>
+        <identifier>1441</identifier>
+        <extended>false</extended>
+        <remote>true</remote>
+        <error>false</error>
+      </SocketCANFrame>
     </xml>
   </testcase>
 


[plc4x] 02/21: Tests for socketcan frames + manual parsing based on ReadBuffer api.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 7447def9596ef3b5dba457e3065ce2f6828446c7
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Thu Aug 20 21:46:34 2020 +0200

    Tests for socketcan frames + manual parsing based on ReadBuffer api.
---
 .../can/src/main/resources/protocols/can/can.mspec | 71 ++++++++++-------
 .../org/apache/plc4x/java/can/CANPlcDriver.java    | 10 +--
 .../apache/plc4x/java/can/helper/HeaderParser.java | 44 +++++++++++
 .../apache/plc4x/java/can/ManualParserTest.java    | 92 ++++++++++++++++++++++
 .../org/apache/plc4x/java/can/SocketCANTest.java   | 30 +++++++
 .../resources/testsuite/SocketCANTestSuite.xml     | 54 +++++++++++++
 6 files changed, 267 insertions(+), 34 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/can.mspec b/protocols/can/src/main/resources/protocols/can/can.mspec
index bbee418..113e304 100644
--- a/protocols/can/src/main/resources/protocols/can/can.mspec
+++ b/protocols/can/src/main/resources/protocols/can/can.mspec
@@ -18,22 +18,19 @@
  */
 
 [type 'CANFrame'
+    [simple CANHeader 'header']
     [simple uint 11 'identifier']
-    [simple bit 'remoteTransmissionRequest']
-    [discriminator bit 'extended']
-    [typeSwitch 'extended'
-        ['false' CANDataFrame
-            [reserved uint 1 '0x0']
-        ]
-        ['true' ExtendedCANFrame
-            [simple uint 18 'extensionId']
-            [simple bit 'extensionRemoteTransmissionRequest']
-            [reserved uint 2 '0x0']
-        ]
-    ]
-    [simple uint 4 'length']
-    [array uint 8 'data' count 'length']
-    [simple uint 15 'crc']
+    [simple bit 'extended']
+    [simple bit 'remote']
+    [simple bit 'error']
+]
+
+[type 'CANHeader'
+    [simple uint 11 'identifier']
+    [simple bit 'extended']
+    [simple bit 'remote']
+    [simple bit 'error']
+
 ]
 
 /* These are structures defined in linux kernel, provided here just for information purposes
@@ -55,23 +52,39 @@ struct canfd_frame {
 };
 */
 
+[type 'OtherSocketCANFrame'
+    [simple int 32 'rawId']
+    [virtual bit 'extended'
+        'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isRemote", rawId)'
+    ]
+    [virtual bit 'remote'
+        'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isRemote", rawId)'
+    ]
+    [virtual bit 'error'
+        'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isError", rawId)'
+    ]
+//    [typeSwitch 'extended'
+//        ['true' ExtendedOtherSocketCanFrame
+//            [simple uint 8 'flags']
+//        ]
+//        ['false' ExtendedOtherSocketCanFrame
+            [reserved uint 8 '0x0']
+//        ]
+//    ]
+    [reserved uint 8 '0x0']
+    [reserved uint 8 '0x0']
+    [implicit uint 8 'size' 'COUNT(data)']
+    [array int 8 'data' COUNT 'size']
+]
+
 [type 'SocketCANFrame'
-    [simple uint 29 'identifier']
     [simple bit 'extended']
     [simple bit 'remote']
     [simple bit 'error']
-    [implicit uint 8 'length' 'ARRAY_SIZE_IN_BYTES(data)']
-    [typeSwitch 'extended', 'identifier'
-        ['true' SocketCANFDFrame
-            [simple uint 8 'flags']
-            [reserved uint 8 '0x0']
-            [reserved uint 8 '0x0']
-        ]
-        ['false' ScoketCANFrame
-            [reserved uint 8 '0x0']
-            [reserved uint 8 '0x0']
-            [reserved uint 8 '0x0']
-        ]
-    ]
+    [simple uint 29 'identifier']
+    [implicit uint 8 'length' 'COUNT(data)']
+    [reserved uint 8 '0x0'] // flags
+    [reserved uint 8 '0x0'] // padding
+    [reserved uint 8 '0x0'] // padding
     [array int 8 'data' COUNT 'length']
 ]
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
index 4b7eb16..e1a55ed 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
@@ -20,22 +20,17 @@ package org.apache.plc4x.java.can;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufUtil;
-import org.apache.plc4x.java.PlcDriverManager;
-import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.can.context.CANDriverContext;
 import org.apache.plc4x.java.can.field.CANFieldHandler;
 import org.apache.plc4x.java.can.protocol.CANProtocolLogic;
-import org.apache.plc4x.java.can.readwrite.CANFrame;
 import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
-import org.apache.plc4x.java.can.readwrite.io.CANFrameIO;
 import org.apache.plc4x.java.can.readwrite.io.SocketCANFrameIO;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
 import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
 import tel.schich.javacan.CanFrame;
-import tel.schich.javacan.CanId;
 
 import java.util.function.Consumer;
 import java.util.function.ToIntFunction;
@@ -83,9 +78,14 @@ public class CANPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
         @Override
         public int applyAsInt(ByteBuf byteBuf) {
             if (byteBuf.readableBytes() >= 5) {
+
                 System.out.println(ByteBufUtil.prettyHexDump(byteBuf));
                 byte len = byteBuf.getByte(4);
                 System.out.println("Length " + (int) len);
+
+                CanFrame frame = CanFrame.create(byteBuf.nioBuffer());
+                System.out.println(frame);
+
                 return len + 8 /* overhead */;
             }
             return -1; //discard
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
new file mode 100644
index 0000000..3ce7f0f
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
@@ -0,0 +1,44 @@
+package org.apache.plc4x.java.can.helper;
+
+import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+
+public class HeaderParser {
+
+    public static final int EXTENDED_FRAME_FORMAT_FLAG = 0x80000000;
+
+    public static final int REMOTE_TRANSMISSION_FLAG = 0x40000000;
+
+    public static final int ERROR_FRAME_FLAG = 0x20000000;
+
+    public static final int STANDARD_FORMAT_IDENTIFIER_MASK = 0x7ff;
+
+    public static final int EXTENDED_FORMAT_IDENTIFIER_MASK = 0x1fffffff;
+
+    public static int readIdentifier(ReadBuffer buffer) throws ParseException {
+        int identifier = buffer.readInt(32);
+        if ((identifier & EXTENDED_FORMAT_IDENTIFIER_MASK) == 0) {
+            return identifier & STANDARD_FORMAT_IDENTIFIER_MASK;
+        }
+        return identifier & EXTENDED_FORMAT_IDENTIFIER_MASK;
+    }
+
+    public static void writeIdentifier(WriteBuffer buffer, SocketCANFrame frame) throws ParseException {
+
+    }
+
+    public static boolean isExtended(int identifier) {
+        return (identifier & EXTENDED_FRAME_FORMAT_FLAG) != 0;
+    }
+
+    public static boolean isRemote(int identifier) {
+        return (identifier & REMOTE_TRANSMISSION_FLAG) != 0;
+    }
+
+    public static boolean isError(int identifier) {
+        return (identifier & ERROR_FRAME_FLAG) != 0;
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/ManualParserTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/ManualParserTest.java
new file mode 100644
index 0000000..11c6ca6
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/ManualParserTest.java
@@ -0,0 +1,92 @@
+package org.apache.plc4x.java.can;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ManualParserTest {
+
+    public static final int EXTENDED_FRAME_FORMAT_FLAG = 0b10000000_00000000_00000000_00000000;
+    public static final int REMOTE_TRANSMISSION_FLAG = 0b01000000_00000000_00000000_00000000;
+    public static final int ERROR_FRAME_FLAG = 0b00100000_00000000_00000000_00000000;
+
+    public static final int STANDARD_FORMAT_IDENTIFIER_MASK = 0b00000000_00000000_00000111_11111111;
+    public static final int EXTENDED_FORMAT_IDENTIFIER_MASK = 0b00011111_11111111_11111111_11111111;
+
+    // cansend 5A1#11.2233.44556677.88
+    String STANDARD = "a1050000080000001122334455667788";
+
+    // cansend 1E6EC676#05.05.1F.26.C3
+    String EXTENDED = "76c66e9e0500000005051f26c3000000";
+
+    @Test
+    public void readBufferTest() throws Exception {
+        ReadBuffer buffer = new ReadBuffer(new byte[]{(byte) 0xA1, 0x05, 0x00, 0x00}, true);
+        int value = buffer.readInt(32);
+
+        assertEquals(value, 0x5A1);
+    }
+
+    @Test
+    public void standardFrameParser() throws Exception {
+        SocketCanFrameStub frame = parse(STANDARD);
+        //System.out.println(frame);
+
+        assertEquals(frame.id, 0x5A1);
+        assertEquals(frame.extended, false);
+        assertEquals(frame.remote, false);
+        assertEquals(frame.error, false);
+        assertEquals(frame.data.length, 8);
+    }
+
+    @Test
+    public void extendedFrameParser() throws Exception {
+        SocketCanFrameStub frame = parse(EXTENDED);
+        //System.out.println(frame);
+
+        assertEquals(frame.id, 0x1e6ec676);
+        assertEquals(frame.extended, true);
+        assertEquals(frame.remote, false);
+        assertEquals(frame.error, false);
+        assertEquals(frame.data.length, 5);
+    }
+
+    public final static SocketCanFrameStub parse(String hex) throws Exception {
+        byte[] input = Hex.decodeHex(hex.toCharArray());
+
+        ReadBuffer readBuffer = new ReadBuffer(input, true);
+        int rawId = readBuffer.readInt(32);
+        boolean extended = (rawId & EXTENDED_FRAME_FORMAT_FLAG) != 0;
+        boolean remote = (rawId & REMOTE_TRANSMISSION_FLAG) != 0;
+        boolean error = (rawId & ERROR_FRAME_FLAG) != 0;
+        int id = extended ? (rawId & EXTENDED_FORMAT_IDENTIFIER_MASK) : (rawId & STANDARD_FORMAT_IDENTIFIER_MASK);
+        int length = readBuffer.readByte(8);
+        byte[] data = readBuffer.getBytes(8, 8 + length);
+
+        return new SocketCanFrameStub(
+            id, extended, remote, error, data
+        );
+    }
+
+    static class SocketCanFrameStub {
+        public int id;
+        public boolean extended;
+        public boolean remote;
+        public boolean error;
+        public byte[] data;
+
+        public SocketCanFrameStub(int id, boolean extended, boolean remote, boolean error, byte[] data) {
+            this.id = id;
+            this.extended = extended;
+            this.remote = remote;
+            this.error = error;
+            this.data = data;
+        }
+
+        public String toString() {
+            return "CAN Frame ID=" + Integer.toHexString(id) + ", extended=" + extended + ", remote=" + remote + ", error=" + error + ", data=" + Hex.encodeHexString(data);
+        }
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/SocketCANTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/SocketCANTest.java
new file mode 100644
index 0000000..270b45a
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/SocketCANTest.java
@@ -0,0 +1,30 @@
+/*
+  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.can;
+
+import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
+
+public class SocketCANTest extends ParserSerializerTestsuiteRunner {
+
+    public SocketCANTest() {
+        super("/testsuite/SocketCANTestSuite.xml");
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
new file mode 100644
index 0000000..774563b
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<test:testsuite xmlns:test="https://plc4x.apache.org/schemas/parser-serializer-testsuite.xsd">
+
+  <name>CAN Standard Format Frame</name>
+
+  <testcase>
+    <name>Extended frame 1E6EC676#05.05.1F.26.C3</name>
+    <raw>76c66e9e0500000005051f26c3000000</raw>
+    <root-type>SocketCANFrame</root-type>
+    <xml>
+      <SocketCANFDFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+        <identifier>510576246</identifier>
+        <extended>true</extended>
+        <remote>false</remote>
+        <error>true</error>
+        <data></data>
+      </SocketCANFDFrame>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>Standard frame 5A1#11.2233.44556677.88</name>
+    <raw>a1050000080000001122334455667788</raw>
+    <root-type>SocketCANFrame</root-type>
+    <xml>
+      <ScoketCANSFFrame className="org.apache.plc4x.java.can.readwrite.ScoketCANFrame">
+        <identifier>1441</identifier>
+        <extended>false</extended>
+        <remote>false</remote>
+        <error>false</error>
+        <data></data>
+      </ScoketCANSFFrame>
+    </xml>
+  </testcase>
+
+</test:testsuite>
\ No newline at end of file


[plc4x] 18/21: Working on SDO upload (read) operation coordination.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit d7a65709b5c79d192e55b96f27ec9dde47890486
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Fri Oct 9 11:46:04 2020 +0200

    Working on SDO upload (read) operation coordination.
---
 .../spi/transaction/RequestTransactionManager.java | 23 +++++-
 .../src/main/resources/protocols/can/canopen.mspec |  9 +-
 sandbox/test-java-can-driver/pom.xml               |  4 +
 .../api/conversation/canopen/CANConversation.java  |  4 +-
 .../conversation/canopen/CANOpenConversation.java  | 14 +++-
 .../canopen/SDOUploadConversation.java             | 95 ++++++++++++++++------
 .../java/can/configuration/CANConfiguration.java   |  6 +-
 .../plc4x/java/can/field/CANOpenPDOField.java      | 19 ++++-
 .../java/can/protocol/CANOpenProtocolLogic.java    | 76 ++++++++++++-----
 .../can/protocol/CANOpenSubscriptionHandle.java    | 17 +++-
 .../java/can/socketcan/SocketCANConversation.java  | 32 ++++----
 11 files changed, 224 insertions(+), 75 deletions(-)

diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/transaction/RequestTransactionManager.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/transaction/RequestTransactionManager.java
index e49953c..5fb2ed5 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/transaction/RequestTransactionManager.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/transaction/RequestTransactionManager.java
@@ -21,6 +21,7 @@ package org.apache.plc4x.java.spi.transaction;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
 
 import java.util.Objects;
 import java.util.Queue;
@@ -173,7 +174,8 @@ public class RequestTransactionManager {
         }
 
         public void submit(Runnable operation) {
-            this.setOperation(operation);
+            logger.trace("Submission of transaction {}", transactionId);
+            this.setOperation(new TransactionOperation(transactionId, operation));
             this.parent.submit(this);
         }
 
@@ -189,6 +191,25 @@ public class RequestTransactionManager {
         public int hashCode() {
             return Objects.hash(transactionId);
         }
+
     }
 
+    static class TransactionOperation implements Runnable {
+        private final int transactionId;
+        private final Runnable delegate;
+
+        public TransactionOperation(int transactionId, Runnable delegate) {
+            this.transactionId = transactionId;
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void run() {
+            try (final MDC.MDCCloseable closeable = MDC.putCloseable("plc4x.transactionId", Integer.toString(transactionId))) {
+                logger.trace("Start execution of transaction {}", transactionId);
+                delegate.run();
+                logger.trace("Completed execution of transaction {}", transactionId);
+            }
+        }
+    }
 }
diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index 10afdf9..7886b88 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -353,4 +353,11 @@
             ]
         ]
     ]
-]
\ No newline at end of file
+]
+
+// utility type quickly write data for mapped/manufacturer PDOs
+[type 'CANOpenMPDO'
+    [simple uint 8 'node']
+    [simple IndexAddress 'address']
+    [array int 8 'data' COUNT '4']
+]
diff --git a/sandbox/test-java-can-driver/pom.xml b/sandbox/test-java-can-driver/pom.xml
index e3e72c4..4ed0eaa 100644
--- a/sandbox/test-java-can-driver/pom.xml
+++ b/sandbox/test-java-can-driver/pom.xml
@@ -145,6 +145,10 @@
       <artifactId>jackson-dataformat-xml</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+    </dependency>
   </dependencies>
 
 </project>
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
index 6df8b0c..be4675b 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
@@ -9,9 +9,11 @@ import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTr
 
 public interface CANConversation<W extends CANFrame> {
 
+    int getNodeId();
+
     CANFrameBuilder<W> frameBuilder();
 
-    void send(W frame, BiConsumer<RequestTransaction, SendRequestContext<W>> callback);
+    void send(RequestTransaction transaction, W frame, BiConsumer<RequestTransaction, SendRequestContext<W>> callback);
 
 
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
index abe6aa0..fee418d 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
@@ -9,16 +9,23 @@ import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import java.util.concurrent.CompletableFuture;
 import java.util.function.BiConsumer;
 
 public class CANOpenConversation<W extends CANFrame> {
 
+    private final Logger logger = LoggerFactory.getLogger(CANOpenConversation.class);
+    private final RequestTransaction transaction;
     private final int node;
     private final CANConversation<W> delegate;
 
-    public CANOpenConversation(int node, CANConversation<W> delegate) {
+    public CANOpenConversation(RequestTransaction transaction, int node, CANConversation<W> delegate) {
+        this.transaction = transaction;
         this.node = node;
         this.delegate = delegate;
     }
@@ -30,7 +37,8 @@ public class CANOpenConversation<W extends CANFrame> {
     public void send(CANOpenService service, CANOpenPayload payload, BiConsumer<RequestTransaction, SendRequestContext<CANOpenPayload>> callback) {
         CANFrameBuilder<W> builder = delegate.frameBuilder();
         W frame = builder.node(service.getMin() + node).data(serialize(payload)).build();
-        delegate.send(frame, (tx, ctx) -> {
+        logger.info("Request data under transaction {}", transaction);
+        delegate.send(transaction, frame, (tx, ctx) -> {
             SendRequestContext<CANOpenPayload> unwrap = ctx
 //                .onError((response, error) -> {
 //                    System.err.println("Unexpected frame " + response + " " + error);
@@ -38,6 +46,8 @@ public class CANOpenConversation<W extends CANFrame> {
             .unwrap(CANOpenConversation.this::deserialize);
             callback.accept(tx, unwrap);
         });
+
+
     }
 
     private CANOpenPayload deserialize(CANFrame frame) {
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
index cb2d778..0bb0998 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
@@ -7,11 +7,16 @@ import org.apache.plc4x.java.can.api.segmentation.accumulator.ByteStorage;
 import org.apache.plc4x.java.canopen.readwrite.*;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
 import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import java.util.concurrent.CompletableFuture;
 import java.util.function.BiConsumer;
 
 public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversationBase {
 
+    private final Logger logger = LoggerFactory.getLogger(SDOUploadConversation.class);
     private final SDOConversation<W> delegate;
     private final IndexAddress address;
     private final CANOpenDataType type;
@@ -22,39 +27,56 @@ public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversati
         this.type = type;
     }
 
-    public void execute(BiConsumer<PlcValue, Throwable> receiver) throws PlcException {
+    public void execute(CompletableFuture<PlcValue> receiver) throws PlcException {
         SDOInitiateUploadRequest rq = new SDOInitiateUploadRequest(address);
 
         delegate.send(rq, (tx, ctx) ->
-            ctx
-//            .onError((response, error) -> {
-//                System.err.println("Unexpected frame " + response + " " + error);
-//                receiver.accept(null, error);
-//            })
-            .unwrap(CANOpenSDOResponse::getResponse)
-            .onError(((response, error) -> {
-                if (response instanceof SDOAbortResponse) {
-                    SDOAbortResponse abort = (SDOAbortResponse) response;
+            ctx.onError((response, error) -> {
+                System.err.println("Unexpected frame " + response + " " + error);
+                if (error != null) {
+                    receiver.completeExceptionally(error);
+                    return;
+                }
+                if (response.getResponse() instanceof SDOAbortResponse) {
+                    SDOAbortResponse abort = (SDOAbortResponse) response.getResponse();
                     SDOAbort sdoAbort = abort.getAbort();
-                    receiver.accept(null, new PlcException("Could not read value. Remote party reported code " + sdoAbort.getCode()));
-                } else {
-                    receiver.accept(null, error);
+                    receiver.completeExceptionally(new PlcException("Could not read value. Remote party reported code " + sdoAbort.getCode()));
                 }
-            }))
+            })
+            .check(reply -> {
+                logger.warn("Received answer {}", reply);
+                return true;
+            })
+            .unwrap(CANOpenSDOResponse::getResponse).check(reply -> {
+                logger.warn("Received answer {}", reply);
+                return true;
+            })
+            .check(reply -> {
+                logger.warn("Received answer {}", reply);
+                return true;
+            })
             .only(SDOInitiateUploadResponse.class)
-            .check(r -> r.getAddress().equals(address))
+            .check(resp -> {
+                logger.warn("Checking if reply address {}/{} matches {}/{}: {}",
+                    Integer.toHexString(resp.getAddress().getIndex()), Integer.toHexString(resp.getAddress().getSubindex()),
+                    Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()),
+                    resp.getAddress().equals(address)
+                );
+                return resp.getAddress().equals(address);
+            })
             .handle(response -> {
-                handle(receiver, response);
+                handle(tx, receiver, response);
             })
         );
     }
 
-    private void handle(BiConsumer<PlcValue, Throwable> receiver, SDOInitiateUploadResponse answer) {
+    private void handle(RequestTransactionManager.RequestTransaction tx, CompletableFuture<PlcValue> receiver, SDOInitiateUploadResponse answer) {
         BiConsumer<Integer, byte[]> valueCallback = (length, bytes) -> {
             try {
-                receiver.accept(decodeFrom(bytes, type, length), null);
-            } catch (ParseException e) {
-                receiver.accept(null, e);
+                final PlcValue decodedValue = decodeFrom(bytes, type, length);
+                receiver.complete(decodedValue);
+            } catch (ArrayIndexOutOfBoundsException | ParseException e) {
+                receiver.completeExceptionally(e);
             }
         };
 
@@ -62,32 +84,51 @@ public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversati
             SDOInitiateExpeditedUploadResponse payload = (SDOInitiateExpeditedUploadResponse) answer.getPayload();
             valueCallback.accept(payload.getData().length, payload.getData());
         } else if (answer.getPayload() instanceof SDOInitiateSegmentedUploadResponse) {
+            logger.info("Beginning of segmented operation for address {}/{}", Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()));
             ByteStorage.SDOUploadStorage storage = new ByteStorage.SDOUploadStorage();
             storage.append(answer);
 
             SDOInitiateSegmentedUploadResponse segment = (SDOInitiateSegmentedUploadResponse) answer.getPayload();
             fetch(storage, valueCallback, receiver, false, Long.valueOf(segment.getBytes()).intValue());
         } else {
-            receiver.accept(null, new PlcException("Unsupported SDO operation kind."));
+            receiver.completeExceptionally(new PlcException("Unsupported SDO operation kind."));
         }
     }
 
-    private void fetch(ByteStorage.SDOUploadStorage storage, BiConsumer<Integer, byte[]> valueCallback, BiConsumer<PlcValue, Throwable> receiver, boolean toggle, int size) {
+    private void fetch(ByteStorage.SDOUploadStorage storage, BiConsumer<Integer, byte[]> valueCallback, CompletableFuture<PlcValue> receiver, boolean toggle, int size) {
+        logger.info("Request next data block for address {}/{}", Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()));
         delegate.send(new SDOSegmentUploadRequest(toggle), (tx, ctx) -> {
             ctx.unwrap(CANOpenSDOResponse::getResponse)
-                .only(SDOSegmentUploadResponse.class)
                 .onError((response, error) -> {
                     System.out.println("Unexpected frame " + response + " " + error);
-                    receiver.accept(null, error);
+                    if (error != null) {
+                        receiver.completeExceptionally(error);
+                        return;
+                    }
+
+                    if (response instanceof SDOAbortResponse) {
+                        SDOAbortResponse abort = (SDOAbortResponse) response;
+                        SDOAbort sdoAbort = abort.getAbort();
+                        receiver.completeExceptionally(new PlcException("Could not read value. Remote party reported code " + sdoAbort.getCode()));
+                    }
                 })
+                .only(SDOSegmentUploadResponse.class)
                 .check(r -> r.getToggle() == toggle)
-                .handle(reply -> {
-                    storage.append(reply);
+                .handle(response -> {
+//                    if (!reply.getToggle() == toggle) { // toggle flag is wrong, abort transaction
+//                        logger.info("Received invalid answer from party for {}", address);
+//                        delegate.send(new SDOAbortRequest(new SDOAbort(address, 0x100)), (tx2, ctx2) -> {});
+//                        return;
+//                    }
+
+                    storage.append(response);
 
-                    if (reply.getLast()) {
+                    if (response.getLast()) {
                         // validate size
+                        logger.trace("Completed reading of data from {}/{}, collected {}, wanted {}", Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()), storage.size(), size);
                         valueCallback.accept(Long.valueOf(size).intValue(), storage.get());
                     } else {
+                        logger.trace("Continue reading of data from {}/{}, collected {}, wanted {}", Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()), storage.size(), size);
                         fetch(storage, valueCallback, receiver, !toggle, size);
                     }
                 });
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
index fcb7194..af4188a 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
@@ -28,7 +28,7 @@ public class CANConfiguration implements Configuration, CANTransportConfiguratio
     private int nodeId;
 
     @ConfigurationParameter
-    private boolean hearbeat;
+    private boolean heartbeat;
 
     public int getNodeId() {
         return nodeId;
@@ -39,11 +39,11 @@ public class CANConfiguration implements Configuration, CANTransportConfiguratio
     }
 
     public boolean isHeartbeat() {
-        return hearbeat;
+        return heartbeat;
     }
 
     public void setHeartbeat(boolean heartbeat) {
-        this.hearbeat = heartbeat;
+        this.heartbeat = heartbeat;
     }
 
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
index f65e5b3..2761260 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
@@ -20,17 +20,21 @@ package org.apache.plc4x.java.can.field;
 
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 
+import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class CANOpenPDOField extends CANOpenField {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("PDO:" + CANOpenField.NODE_PATTERN + ":(?<canDataType>\\w+)(\\[(?<numberOfElements>\\d)])?");
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<pdo>(?:RECEIVE|TRANSMIT)_PDO_[1-4]):" + CANOpenField.NODE_PATTERN + ":(?<canDataType>\\w+)(\\[(?<numberOfElements>\\d)])?");
+    private final CANOpenService service;
     private final CANOpenDataType canOpenDataType;
 
-    public CANOpenPDOField(int node, CANOpenDataType canOpenDataType) {
+    public CANOpenPDOField(int node, CANOpenService service, CANOpenDataType canOpenDataType) {
         super(node);
+        this.service = service;
         this.canOpenDataType = canOpenDataType;
     }
 
@@ -55,10 +59,19 @@ public class CANOpenPDOField extends CANOpenField {
         Matcher matcher = getMatcher(addressString);
         int nodeId = Integer.parseInt(matcher.group("nodeId"));
 
+        String pdo = matcher.group("pdo");
+        CANOpenService service = CANOpenService.valueOf(pdo);
+        if (service == null) {
+            throw new IllegalArgumentException("Invalid PDO detected " + pdo);
+        }
+
         String canDataTypeString = matcher.group("canDataType");
         CANOpenDataType canOpenDataType = CANOpenDataType.valueOf(canDataTypeString);
 
-        return new CANOpenPDOField(nodeId, canOpenDataType);
+        return new CANOpenPDOField(nodeId, service, canOpenDataType);
     }
 
+    public CANOpenService getService() {
+        return service;
+    }
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index d96fe19..e384d0a 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -18,6 +18,8 @@ under the License.
 */
 package org.apache.plc4x.java.can.protocol;
 
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
 import org.apache.plc4x.java.api.model.PlcField;
@@ -38,7 +40,10 @@ import org.apache.plc4x.java.can.field.CANOpenField;
 import org.apache.plc4x.java.can.field.CANOpenPDOField;
 import org.apache.plc4x.java.can.field.CANOpenSDOField;
 import org.apache.plc4x.java.can.socketcan.SocketCANConversation;
-import org.apache.plc4x.java.canopen.readwrite.*;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPDOPayload;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.IndexAddress;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenHeartbeatPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
@@ -61,6 +66,8 @@ import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Collection;
@@ -91,8 +98,6 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     @Override
     public void setConfiguration(CANConfiguration configuration) {
         this.configuration = configuration;
-        // Set the transaction manager to allow only one message at a time.
-        this.tm = new RequestTransactionManager(1);
     }
 
     @Override
@@ -135,7 +140,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     @Override
     public void setContext(ConversationContext<SocketCANFrame> context) {
         super.setContext(context);
-        this.conversation = new SocketCANConversation(tm, context);
+        this.conversation = new SocketCANConversation(configuration.getNodeId(), context);
     }
 
     private SocketCANFrame createFrame(CANOpenHeartbeatPayload state) throws ParseException {
@@ -171,7 +176,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     private void writeInternally(InternalPlcWriteRequest writeRequest, CANOpenSDOField field, CompletableFuture<PlcWriteResponse> response) {
-        CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(field.getNodeId(), conversation);
+        final RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(transaction, field.getNodeId(), conversation);
 
         PlcValue writeValue = writeRequest.getPlcValues().get(0);
 
@@ -180,6 +186,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
             download.execute((value, error) -> {
                 String fieldName = writeRequest.getFieldNames().iterator().next();
                 response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.OK)));
+                transaction.endRequest();
             });
         } catch (Exception e) {
             response.completeExceptionally(e);
@@ -191,10 +198,10 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
         try {
             String fieldName = writeRequest.getFieldNames().iterator().next();
-            //
-            WriteBuffer buffer = DataItemIO.staticSerialize(writeValue, field.getCanOpenDataType(), writeValue.getLength() / 8, true);
+            WriteBuffer buffer = DataItemIO.staticSerialize(writeValue, field.getCanOpenDataType(), writeValue.getLength(), true);
             if (buffer != null) {
-                context.sendToWire(new SocketCANFrame(field.getNodeId(), buffer.getData()));
+                int cob = field.getService().getMin() + field.getNodeId();
+                context.sendToWire(new SocketCANFrame(cob, buffer.getData()));
                 response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.OK)));
             } else {
                 response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.INVALID_DATA)));
@@ -252,16 +259,27 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     private void readInternally(InternalPlcReadRequest readRequest, CANOpenSDOField field, CompletableFuture<PlcReadResponse> response) {
-        CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(field.getNodeId(), conversation);
-
-        SDOUploadConversation<CANFrame> upload = canopen.sdo().upload(new IndexAddress(field.getIndex(), field.getSubIndex()), field.getCanOpenDataType());
         try {
-            upload.execute((value, error) -> {
+            final RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+            CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(transaction, field.getNodeId(), conversation);
+            System.out.println("----> Submit read " + field.getIndex() + "/" + field.getSubIndex() + " from " + field.getNodeId() + " " + transaction);
+            SDOUploadConversation<CANFrame> upload = canopen.sdo().upload(new IndexAddress(field.getIndex(), field.getSubIndex()), field.getCanOpenDataType());
+            CompletableFuture<PlcValue> callback = new CompletableFuture<>();
+            callback.whenComplete((value, error) -> {
+                System.out.println("<---- Received reply " + field.getIndex() + "/" + field.getSubIndex() + " from " + field.getNodeId() + " " + value + "/" + error + " " + transaction);
+                if (error != null) {
+                    response.completeExceptionally(error);
+                    transaction.endRequest();
+                    return;
+                }
+
                 String fieldName = readRequest.getFieldNames().iterator().next();
                 Map<String, ResponseItem<PlcValue>> fields = new HashMap<>();
                 fields.put(fieldName, new ResponseItem<>(PlcResponseCode.OK, value));
                 response.complete(new DefaultPlcReadResponse(readRequest, fields));
+                transaction.endRequest();
             });
+            upload.execute(callback);
         } catch (Exception e) {
             response.completeExceptionally(e);
         }
@@ -275,13 +293,23 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
         CANOpenDriverContext.CALLBACK.receive(msg);
 
         if (service != null) {
-            logger.info("Decoded CANOpen {} from {}, message {}", service, Math.abs(service.getMin() - msg.getIdentifier()), payload);
+            int nodeId = Math.abs(service.getMin() - msg.getIdentifier());
 
             if (service.getPdo() && payload instanceof CANOpenPDOPayload) {
-                logger.info("Broadcasting PDO to subscribers");
-                publishEvent(msg.getIdentifier(), (CANOpenPDOPayload) payload);
+                publishEvent(service, nodeId, (CANOpenPDOPayload) payload);
+            } else {
+                String hex = "";
+                if (logger.isInfoEnabled()) {
+                    try {
+                        final WriteBuffer buffer = new WriteBuffer(payload.getLengthInBytes(), true);
+                        CANOpenPayloadIO.staticSerialize(buffer, payload);
+                        hex = Hex.encodeHexString(buffer.getData());
+                    } catch (ParseException e) {
+                        e.printStackTrace();
+                    }
+                }
+                logger.info("Decoded CANOpen {} from {}, message {}, {}", service, nodeId, payload, hex);
             }
-
         } else {
             logger.info("CAN message {}, {}", msg.getIdentifier(), msg);
         }
@@ -296,7 +324,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 //        }
     }
 
-    private void publishEvent(int nodeId, CANOpenPDOPayload payload) {
+    private void publishEvent(CANOpenService service, int nodeId, CANOpenPDOPayload payload) {
+        CANOpenSubscriptionHandle dispatchedHandle = null;
         for (Map.Entry<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> entry : consumers.entrySet()) {
             DefaultPlcConsumerRegistration registration = entry.getKey();
             Consumer<PlcSubscriptionEvent> consumer = entry.getValue();
@@ -305,7 +334,10 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
                 if (handler instanceof CANOpenSubscriptionHandle) {
                     CANOpenSubscriptionHandle handle = (CANOpenSubscriptionHandle) handler;
 
-                    if (handle.matches(nodeId)) {
+                    if (handle.matches(service, nodeId)) {
+                        logger.trace("Dispatching notification {} for node {} to {}", service, nodeId, handle);
+                        dispatchedHandle = handle;
+
                         CANOpenPDOField field = handle.getField();
                         byte[] data = payload.getPdo().getData();
                         try {
@@ -329,10 +361,15 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
                             );
                             consumer.accept(event);
                         }
+                    } else {
                     }
                 }
             }
         }
+
+        if (dispatchedHandle == null) {
+            logger.trace("Could not find subscription matching {} and node {}", service, nodeId);
+        }
     }
 
     @Override
@@ -362,8 +399,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
     private int cobId(int nodeId, CANOpenService service) {
         // form 32 bit socketcan identifier
-        return (nodeId << 24) & 0xff000000 |
-            (service.getValue() << 16 ) & 0x00ff0000;
+        return service.getMin() + nodeId;
     }
 
     private CANOpenService serviceId(int cobId) {
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
index 07ecb3b..9bfd688 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
@@ -1,6 +1,7 @@
 package org.apache.plc4x.java.can.protocol;
 
 import org.apache.plc4x.java.can.field.CANOpenPDOField;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.spi.messages.PlcSubscriber;
 import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
 
@@ -14,8 +15,11 @@ public class CANOpenSubscriptionHandle extends DefaultPlcSubscriptionHandle {
         this.field = field;
     }
 
-    public boolean matches(int identifier) {
-        return field.getNodeId() == 0 || field.getNodeId() == identifier;
+    public boolean matches(CANOpenService service, int identifier) {
+        if (field.getService() != service) {
+            return false;
+        }
+        return field.getNodeId() == identifier;
     }
 
     public String getName() {
@@ -25,4 +29,13 @@ public class CANOpenSubscriptionHandle extends DefaultPlcSubscriptionHandle {
     public CANOpenPDOField getField() {
         return field;
     }
+
+    public String toString() {
+        return "CANOpenSubscriptionHandle [service=" + field.getService() + ", node=" + intAndHex(field.getNodeId()) + ", cob=" + intAndHex(field.getService().getMin() + field.getNodeId()) + "]";
+    }
+
+    private static String intAndHex(int val) {
+        return val + "(0x" + Integer.toHexString(val) + ")";
+    }
+
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
index 8b01c2a..c35843d 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
@@ -7,7 +7,6 @@ import org.apache.plc4x.java.can.api.conversation.canopen.CANFrameBuilder;
 import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
-import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
 
 import java.time.Duration;
@@ -15,32 +14,35 @@ import java.util.function.BiConsumer;
 
 public class SocketCANConversation implements CANConversation<CANFrame> {
 
-    private final RequestTransactionManager tm;
+    private final int nodeId;
     private final ConversationContext<SocketCANFrame> context;
 
-    public SocketCANConversation(RequestTransactionManager tm, ConversationContext<SocketCANFrame> context) {
-        this.tm = tm;
+    public SocketCANConversation(int nodeId, ConversationContext<SocketCANFrame> context) {
+        this.nodeId = nodeId;
         this.context = context;
     }
 
     @Override
+    public int getNodeId() {
+        return nodeId;
+    }
+
+    @Override
     public CANFrameBuilder<CANFrame> frameBuilder() {
         return new SocketCANFrameBuilder();
     }
 
     @Override
-    public void send(CANFrame frame, BiConsumer<RequestTransaction, SendRequestContext<CANFrame>> callback) {
+    public void send(RequestTransaction transaction, CANFrame frame, BiConsumer<RequestTransaction, SendRequestContext<CANFrame>> callback) {
         if (frame instanceof SocketCANDelegateFrame) {
-            RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
-
-            ConversationContext.SendRequestContext<CANFrame> ctx = context.sendRequest(((SocketCANDelegateFrame) frame).getFrame())
-                .expectResponse(SocketCANFrame.class, Duration.ofSeconds(10L))
-//                .onError((response, error) -> {
-//                    System.err.println("Unexpected frame " + response + " " + error);
-//                })
-                .unwrap(SocketCANDelegateFrame::new);
-            //return CompletableFuture.completedFuture(new SocketCANTransactionContext<>(transaction, ctx));
-            callback.accept(transaction, ctx);
+            System.out.println("-----> Sending request frame " + transaction);
+            transaction.submit(() -> {
+                ConversationContext.SendRequestContext<CANFrame> ctx = context.sendRequest(((SocketCANDelegateFrame) frame).getFrame())
+                    .expectResponse(SocketCANFrame.class, Duration.ofSeconds(10L))
+                    .unwrap(SocketCANDelegateFrame::new);
+                System.out.println("-----> Frame been sent " + transaction);
+                callback.accept(transaction, ctx);
+            });
             return;
         }
         throw new PlcRuntimeException("Unsupported frame type " + frame);


[plc4x] 17/21: CANopen microstone - PDO & subscriptions.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 6aa220e3653e070ba8e8b08567c080a92b17e0ef
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Wed Oct 7 11:31:43 2020 +0200

    CANopen microstone - PDO & subscriptions.
---
 .../resources/templates/java/data-io-template.ftlh |  19 ++-
 .../org/apache/plc4x/java/api/value/PlcValues.java |   4 +
 .../src/main/resources/protocols/can/canopen.mspec |  40 +++---
 .../apache/plc4x/java/can/CANOpenPlcDriver.java    |   5 +
 .../apache/plc4x/java/can/field/CANOpenField.java  |   2 +
 .../plc4x/java/can/field/CANOpenFieldHandler.java  |  20 ++-
 .../plc4x/java/can/field/CANOpenPDOField.java      |  64 ++++++++++
 .../java/can/protocol/CANOpenProtocolLogic.java    | 137 +++++++++++++++++++--
 .../can/protocol/CANOpenSubscriptionHandle.java    |  28 +++++
 9 files changed, 281 insertions(+), 38 deletions(-)

diff --git a/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh b/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh
index 568fed9..3f680da 100644
--- a/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh
+++ b/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh
@@ -119,10 +119,21 @@ public class ${type.name}IO {
                             <#if helper.isLengthArrayField(field)>
             // Length array
             int _${field.name}Length = ${helper.toParseExpression(field, field.loopExpression, type.parserArguments)};
-            List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}> _${field.name}List = new LinkedList<>();
+            List<PlcValue> _${field.name}List = new LinkedList<>();
             int ${field.name}EndPos = io.getPos() + _${field.name}Length;
             while(io.getPos() < ${field.name}EndPos) {
-                _${field.name}List.add(<#if helper.isSimpleTypeReference(field.type)>${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name}IO.staticParse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(field.type, parserArgument?index), true)}) (${helper.toParseExpression(field, parserArgument, type.parserArguments)})<#sep>, </#sep></#list></#if>)</#if>);
+                _${field.name}List.add(
+                    <#if helper.isSimpleTypeReference(field.type)>PlcValues.of(${helper.getReadBufferReadMethodCall(field.type)})
+                    <#else>${field.type.name}IO.staticParse(io
+                        <#if field.params?has_content>,
+                            <#list field.params as parserArgument>
+                                (${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(field.type, parserArgument?index), true)}) (${helper.toParseExpression(field, parserArgument, type.parserArguments)})
+                                <#sep>, </#sep>
+                            </#list>
+                        </#if>
+                    )
+                    </#if>
+                );
                 <#-- After parsing, update the current position, but only if it's needed -->
                                 <#if field.loopExpression.contains("curPos")>
                 curPos = io.getPos() - startPos;
@@ -145,7 +156,6 @@ public class ${type.name}IO {
                 Convert the list into an array. However if the array is of a primitive
                 type we have to iterate over it's elements and explicitly cast them.
                 Otherwise a simple toArray call is fine.
-            -->
                             <#if helper.isSimpleTypeReference(field.type)>
             ${helper.getNonPrimitiveLanguageTypeNameForField(field)}[] ${field.name} = new ${helper.getNonPrimitiveLanguageTypeNameForField(field)}[_${field.name}List.size()];
             for(int i = 0; i < _${field.name}List.size(); i++) {
@@ -154,6 +164,9 @@ public class ${type.name}IO {
                             <#else>
             ${helper.getNonPrimitiveLanguageTypeNameForField(field)}[] ${field.name} = _${field.name}List.toArray(new ${helper.getNonPrimitiveLanguageTypeNameForField(field)}[0]);
                             </#if>
+
+            -->
+            List<?> value = _${field.name}List;
                         </#if>
                     <#break>
                     <#case "const">
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java
index 414eb62..03b3761 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcValues.java
@@ -441,6 +441,10 @@ public class PlcValues {
         if(o == null) {
             return new PlcNull();
         }
+        if (o instanceof Byte) {
+            return new PlcBYTE((Byte) o);
+        }
+
         try {
             String simpleName = o.getClass().getSimpleName();
             Class<?> clazz = o.getClass();
diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index 3b1e49d..10afdf9 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -272,64 +272,64 @@
 
 [dataIo 'DataItem' [CANOpenDataType 'dataType', int 32 'size']
     [typeSwitch 'dataType'
-        ['CANOpenDataType.BOOLEAN' Boolean
+        ['CANOpenDataType.BOOLEAN' BOOL
             [simple bit 'value']
         ]
-        ['CANOpenDataType.UNSIGNED8' Integer
+        ['CANOpenDataType.UNSIGNED8' USINT
             [simple uint 8 'value']
         ]
-        ['CANOpenDataType.UNSIGNED16' Integer
+        ['CANOpenDataType.UNSIGNED16' UINT
             [simple uint 16 'value']
         ]
-        ['CANOpenDataType.UNSIGNED24' Long
+        ['CANOpenDataType.UNSIGNED24' UDINT
             [simple uint 24 'value']
         ]
-        ['CANOpenDataType.UNSIGNED32' Long
+        ['CANOpenDataType.UNSIGNED32' UDINT
             [simple uint 32 'value']
         ]
-        ['CANOpenDataType.UNSIGNED40' BigInteger
+        ['CANOpenDataType.UNSIGNED40' ULINT
             [simple uint 40 'value']
         ]
-        ['CANOpenDataType.UNSIGNED48' BigInteger
+        ['CANOpenDataType.UNSIGNED48' ULINT
             [simple uint 48 'value']
         ]
-        ['CANOpenDataType.UNSIGNED56' BigInteger
+        ['CANOpenDataType.UNSIGNED56' ULINT
             [simple uint 56 'value']
         ]
-        ['CANOpenDataType.UNSIGNED64' BigInteger
+        ['CANOpenDataType.UNSIGNED64' ULINT
             [simple uint 64 'value']
         ]
-        ['CANOpenDataType.INTEGER8' Integer
+        ['CANOpenDataType.INTEGER8' SINT
             [simple int 8 'value']
         ]
-        ['CANOpenDataType.INTEGER16' Integer
+        ['CANOpenDataType.INTEGER16' INT
             [simple int 16 'value']
         ]
-        ['CANOpenDataType.INTEGER24' Integer
+        ['CANOpenDataType.INTEGER24' DINT
             [simple int 24 'value']
         ]
-        ['CANOpenDataType.INTEGER32' Integer
+        ['CANOpenDataType.INTEGER32' DINT
             [simple int 32 'value']
         ]
-        ['CANOpenDataType.INTEGER40' Long
+        ['CANOpenDataType.INTEGER40' LINT
             [simple int 40 'value']
         ]
-        ['CANOpenDataType.INTEGER48' Long
+        ['CANOpenDataType.INTEGER48' LINT
             [simple int 48 'value']
         ]
-        ['CANOpenDataType.INTEGER56' Long
+        ['CANOpenDataType.INTEGER56' LINT
             [simple int 56 'value']
         ]
-        ['CANOpenDataType.INTEGER64' Long
+        ['CANOpenDataType.INTEGER64' LINT
             [simple int 64 'value']
         ]
-        ['CANOpenDataType.REAL32' Float
+        ['CANOpenDataType.REAL32' REAL
             [simple float 8.23 'value']
         ]
-        ['CANOpenDataType.REAL64' Double
+        ['CANOpenDataType.REAL64' LREAL
             [simple float 11.52 'value']
         ]
-        ['CANOpenDataType.RECORD' List
+        ['CANOpenDataType.RECORD' List [int 32 'size']
             [array int 8 'value' length 'size']
         ]
         ['CANOpenDataType.OCTET_STRING' String
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
index a8a5f7a..c34806f 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
@@ -58,6 +58,11 @@ public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
     }
 
     @Override
+    protected boolean canSubscribe() {
+        return true;
+    }
+
+    @Override
     protected boolean canWrite() {
         return true;
     }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
index 6458848..6917351 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
@@ -44,6 +44,8 @@ public abstract class CANOpenField implements PlcField {
     public static CANOpenField of(String addressString) throws PlcInvalidFieldException {
         if (CANOpenSDOField.matches(addressString)) {
             return CANOpenSDOField.of(addressString);
+        } else if (CANOpenPDOField.matches(addressString)) {
+            return CANOpenPDOField.of(addressString);
         }
 
         throw new PlcInvalidFieldException("Unable to parse address: " + addressString);
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenFieldHandler.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenFieldHandler.java
index cf37533..cb61195 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenFieldHandler.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenFieldHandler.java
@@ -21,13 +21,13 @@ package org.apache.plc4x.java.can.field;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.model.PlcField;
-import org.apache.plc4x.java.api.value.PlcList;
-import org.apache.plc4x.java.api.value.PlcString;
-import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.api.value.*;
 import org.apache.plc4x.java.spi.connection.DefaultPlcFieldHandler;
 import org.apache.plc4x.java.spi.connection.PlcFieldHandler;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.stream.Collectors;
 
 public class CANOpenFieldHandler extends DefaultPlcFieldHandler implements PlcFieldHandler {
@@ -55,4 +55,18 @@ public class CANOpenFieldHandler extends DefaultPlcFieldHandler implements PlcFi
 
         throw new PlcRuntimeException("Invalid encoder for type " + coField.getCanOpenDataType().name());
     }
+
+    @Override
+    public PlcValue encodeByte(PlcField field, Object[] values) {
+        List<PlcValue> resultSet = new ArrayList<>();
+        for (Object item : values) {
+            resultSet.add(PlcValues.of((Byte) item));
+        }
+
+        if (resultSet.size() == 1) {
+            return resultSet.get(0);
+        } else {
+            return new PlcList(resultSet);
+        }
+    }
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
new file mode 100644
index 0000000..f65e5b3
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
@@ -0,0 +1,64 @@
+/*
+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.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CANOpenPDOField extends CANOpenField {
+
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("PDO:" + CANOpenField.NODE_PATTERN + ":(?<canDataType>\\w+)(\\[(?<numberOfElements>\\d)])?");
+    private final CANOpenDataType canOpenDataType;
+
+    public CANOpenPDOField(int node, CANOpenDataType canOpenDataType) {
+        super(node);
+        this.canOpenDataType = canOpenDataType;
+    }
+
+    public CANOpenDataType getCanOpenDataType() {
+        return canOpenDataType;
+    }
+
+    public static boolean matches(String addressString) {
+        return ADDRESS_PATTERN.matcher(addressString).matches();
+    }
+
+    public static Matcher getMatcher(String addressString) throws PlcInvalidFieldException {
+        Matcher matcher = ADDRESS_PATTERN.matcher(addressString);
+        if (matcher.matches()) {
+            return matcher;
+        }
+
+        throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN);
+    }
+
+    public static CANOpenPDOField of(String addressString) {
+        Matcher matcher = getMatcher(addressString);
+        int nodeId = Integer.parseInt(matcher.group("nodeId"));
+
+        String canDataTypeString = matcher.group("canDataType");
+        CANOpenDataType canOpenDataType = CANOpenDataType.valueOf(canDataTypeString);
+
+        return new CANOpenPDOField(nodeId, canOpenDataType);
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index b5aa34e..d96fe19 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -19,8 +19,12 @@ under the License.
 package org.apache.plc4x.java.can.protocol;
 
 import org.apache.plc4x.java.api.messages.*;
+import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
 import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.types.PlcSubscriptionType;
+import org.apache.plc4x.java.api.value.PlcList;
 import org.apache.plc4x.java.api.value.PlcNull;
 import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.can.api.CANFrame;
@@ -31,6 +35,7 @@ import org.apache.plc4x.java.can.api.conversation.canopen.SDOUploadConversation;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.can.context.CANOpenDriverContext;
 import org.apache.plc4x.java.can.field.CANOpenField;
+import org.apache.plc4x.java.can.field.CANOpenPDOField;
 import org.apache.plc4x.java.can.field.CANOpenSDOField;
 import org.apache.plc4x.java.can.socketcan.SocketCANConversation;
 import org.apache.plc4x.java.canopen.readwrite.*;
@@ -49,18 +54,28 @@ import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 import org.apache.plc4x.java.spi.messages.*;
 import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
+import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
+import org.apache.plc4x.java.spi.model.InternalPlcSubscriptionHandle;
+import org.apache.plc4x.java.spi.model.SubscriptionPlcField;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.time.Duration;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 
-public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> implements HasConfiguration<CANConfiguration> {
+public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> implements HasConfiguration<CANConfiguration>, PlcSubscriber {
 
     private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10L);
     private Logger logger = LoggerFactory.getLogger(CANOpenProtocolLogic.class);
@@ -71,6 +86,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     private CANOpenDriverContext canContext;
     private CANConversation<CANFrame> conversation;
 
+    private Map<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> consumers = new ConcurrentHashMap<>();
+
     @Override
     public void setConfiguration(CANConfiguration configuration) {
         this.configuration = configuration;
@@ -130,7 +147,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
         CompletableFuture<PlcWriteResponse> response = new CompletableFuture<>();
         if (writeRequest.getFieldNames().size() != 1) {
-            response.completeExceptionally(new IllegalArgumentException("Unsupported field"));
+            response.completeExceptionally(new IllegalArgumentException("You can write only one field at the time"));
             return response;
         }
 
@@ -140,12 +157,16 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
             return response;
         }
 
-        if (!(field instanceof CANOpenSDOField)) {
-            response.completeExceptionally(new IllegalArgumentException("Only CANOpenSDOField instances are supported"));
+        if (field instanceof CANOpenSDOField) {
+            writeInternally((InternalPlcWriteRequest) writeRequest, (CANOpenSDOField) field, response);
             return response;
-        };
+        }
+        if (field instanceof CANOpenPDOField) {
+            writeInternally((InternalPlcWriteRequest) writeRequest, (CANOpenPDOField) field, response);
+            return response;
+        }
 
-        writeInternally((InternalPlcWriteRequest) writeRequest, (CANOpenSDOField) field, response);
+        response.completeExceptionally(new IllegalArgumentException("Only CANOpenSDOField instances are supported"));
         return response;
     }
 
@@ -158,15 +179,31 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
         try {
             download.execute((value, error) -> {
                 String fieldName = writeRequest.getFieldNames().iterator().next();
-                Map<String, PlcResponseCode> fields = new HashMap<>();
-                fields.put(fieldName, PlcResponseCode.OK);
-                response.complete(new DefaultPlcWriteResponse(writeRequest, fields));
+                response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.OK)));
             });
         } catch (Exception e) {
             response.completeExceptionally(e);
         }
     }
 
+    private void writeInternally(InternalPlcWriteRequest writeRequest, CANOpenPDOField field, CompletableFuture<PlcWriteResponse> response) {
+        PlcValue writeValue = writeRequest.getPlcValues().get(0);
+
+        try {
+            String fieldName = writeRequest.getFieldNames().iterator().next();
+            //
+            WriteBuffer buffer = DataItemIO.staticSerialize(writeValue, field.getCanOpenDataType(), writeValue.getLength() / 8, true);
+            if (buffer != null) {
+                context.sendToWire(new SocketCANFrame(field.getNodeId(), buffer.getData()));
+                response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.OK)));
+            } else {
+                response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.INVALID_DATA)));
+            }
+        } catch (Exception e) {
+            response.completeExceptionally(e);
+        }
+    }
+
     public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
         CompletableFuture<PlcReadResponse> response = new CompletableFuture<>();
         if (readRequest.getFieldNames().size() != 1) {
@@ -190,9 +227,28 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     @Override
-    public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
-        ((InternalPlcSubscriptionRequest) subscriptionRequest).getSubscriptionFields().get(0).getPlcSubscriptionType();
-        return super.subscribe(subscriptionRequest);
+    public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest request) {
+        InternalPlcSubscriptionRequest rq = (InternalPlcSubscriptionRequest) request;
+
+        List<SubscriptionPlcField> fields = rq.getSubscriptionFields();
+
+        Map<String, ResponseItem<PlcSubscriptionHandle>> answers = new LinkedHashMap<>();
+        DefaultPlcSubscriptionResponse response = new DefaultPlcSubscriptionResponse(rq, answers);
+
+        for (Map.Entry<String, SubscriptionPlcField> entry : rq.getSubscriptionPlcFieldMap().entrySet()) {
+            SubscriptionPlcField subscription = entry.getValue();
+            if (subscription.getPlcSubscriptionType() != PlcSubscriptionType.EVENT) {
+                answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.UNSUPPORTED, null));
+            } else if (!(subscription.getPlcField() instanceof CANOpenPDOField)) {
+                answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.INVALID_ADDRESS, null));
+            } else {
+                answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.OK,
+                    new CANOpenSubscriptionHandle(this, entry.getKey(), (CANOpenPDOField) subscription.getPlcField())
+                ));
+            }
+        }
+
+        return CompletableFuture.completedFuture(response);
     }
 
     private void readInternally(InternalPlcReadRequest readRequest, CANOpenSDOField field, CompletableFuture<PlcReadResponse> response) {
@@ -220,6 +276,12 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
         if (service != null) {
             logger.info("Decoded CANOpen {} from {}, message {}", service, Math.abs(service.getMin() - msg.getIdentifier()), payload);
+
+            if (service.getPdo() && payload instanceof CANOpenPDOPayload) {
+                logger.info("Broadcasting PDO to subscribers");
+                publishEvent(msg.getIdentifier(), (CANOpenPDOPayload) payload);
+            }
+
         } else {
             logger.info("CAN message {}, {}", msg.getIdentifier(), msg);
         }
@@ -234,6 +296,57 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 //        }
     }
 
+    private void publishEvent(int nodeId, CANOpenPDOPayload payload) {
+        for (Map.Entry<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> entry : consumers.entrySet()) {
+            DefaultPlcConsumerRegistration registration = entry.getKey();
+            Consumer<PlcSubscriptionEvent> consumer = entry.getValue();
+
+            for (InternalPlcSubscriptionHandle handler : registration.getAssociatedHandles()) {
+                if (handler instanceof CANOpenSubscriptionHandle) {
+                    CANOpenSubscriptionHandle handle = (CANOpenSubscriptionHandle) handler;
+
+                    if (handle.matches(nodeId)) {
+                        CANOpenPDOField field = handle.getField();
+                        byte[] data = payload.getPdo().getData();
+                        try {
+                            PlcValue value = DataItemIO.staticParse(new ReadBuffer(data, true), field.getCanOpenDataType(), data.length);
+                            DefaultPlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(
+                                Instant.now(),
+                                Collections.singletonMap(
+                                    handle.getName(),
+                                    new ResponseItem<>(PlcResponseCode.OK, value)
+                                )
+                            );
+                            consumer.accept(event);
+                        } catch (ParseException e) {
+                            logger.warn("Could not parse data to desired type: {}", field.getCanOpenDataType(), e);
+                            DefaultPlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(
+                                Instant.now(),
+                                Collections.singletonMap(
+                                    handle.getName(),
+                                    new ResponseItem<>(PlcResponseCode.INVALID_DATA, new PlcNull())
+                                )
+                            );
+                            consumer.accept(event);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public PlcConsumerRegistration register(Consumer<PlcSubscriptionEvent> consumer, Collection<PlcSubscriptionHandle> handles) {
+        final DefaultPlcConsumerRegistration consumerRegistration =new DefaultPlcConsumerRegistration(this, consumer, handles.toArray(new InternalPlcSubscriptionHandle[0]));
+        consumers.put(consumerRegistration, consumer);
+        return consumerRegistration;
+    }
+
+    @Override
+    public void unregister(PlcConsumerRegistration registration) {
+        consumers.remove(registration);
+    }
+
     @Override
     public void close(ConversationContext<SocketCANFrame> context) {
 
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
new file mode 100644
index 0000000..07ecb3b
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
@@ -0,0 +1,28 @@
+package org.apache.plc4x.java.can.protocol;
+
+import org.apache.plc4x.java.can.field.CANOpenPDOField;
+import org.apache.plc4x.java.spi.messages.PlcSubscriber;
+import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
+
+public class CANOpenSubscriptionHandle extends DefaultPlcSubscriptionHandle {
+    private final String name;
+    private final CANOpenPDOField field;
+
+    public CANOpenSubscriptionHandle(PlcSubscriber subscriber, String name, CANOpenPDOField field) {
+        super(subscriber);
+        this.name = name;
+        this.field = field;
+    }
+
+    public boolean matches(int identifier) {
+        return field.getNodeId() == 0 || field.getNodeId() == identifier;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public CANOpenPDOField getField() {
+        return field;
+    }
+}


[plc4x] 05/21: Split different flavours of CAN formats.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 36ed9df3f7787764c67b07a4389c574cfe9db08d
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Thu Aug 27 22:59:52 2020 +0200

    Split different flavours of CAN formats.
---
 .../can/{CANProtocol.java => CAN20AProtocol.java}  |  6 +--
 .../can/{CANProtocol.java => CANOpenProtocol.java} |  6 +--
 .../{CANProtocol.java => SocketCANProtocol.java}   |  6 +--
 ...e.plc4x.plugins.codegenerator.protocol.Protocol |  4 +-
 .../src/main/resources/protocols/can/can20a.mspec  | 25 +++++++++
 .../src/main/resources/protocols/can/canopen.mspec | 44 ++++++++++++++++
 .../protocols/can/{can.mspec => socketcan.mspec}   | 61 ----------------------
 sandbox/test-java-can-driver/pom.xml               | 16 +++++-
 .../org/apache/plc4x/java/can/CANPlcDriver.java    |  4 +-
 .../plc4x/java/can/helper/CANOpenHelper.java       | 18 +++++++
 .../apache/plc4x/java/can/helper/HeaderParser.java |  2 +-
 .../plc4x/java/can/protocol/CANProtocolLogic.java  |  2 +-
 .../org/apache/plc4x/java/can/CANOpenTest.java}    | 32 ++++--------
 ...ketCAN20BTestSuite.xml => CANOpenTestSuite.xml} | 18 +++----
 .../resources/testsuite/SocketCAN20ATestSuite.xml  |  6 +--
 .../resources/testsuite/SocketCAN20BTestSuite.xml  |  2 +-
 .../resources/testsuite/SocketCANTestSuite.xml     |  6 +--
 17 files changed, 144 insertions(+), 114 deletions(-)

diff --git a/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CAN20AProtocol.java
similarity index 89%
copy from protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
copy to protocols/can/src/main/java/org/apache/plc4x/protocol/can/CAN20AProtocol.java
index 853fb50..f93d868 100644
--- a/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
+++ b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CAN20AProtocol.java
@@ -27,16 +27,16 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class CANProtocol implements Protocol {
+public class CAN20AProtocol implements Protocol {
 
     @Override
     public String getName() {
-        return "can";
+        return "can20a";
     }
 
     @Override
     public Map<String, TypeDefinition> getTypeDefinitions() throws GenerationException {
-        InputStream schemaInputStream = CANProtocol.class.getResourceAsStream("/protocols/can/can.mspec");
+        InputStream schemaInputStream = CAN20AProtocol.class.getResourceAsStream("/protocols/can/can20a.mspec");
         if(schemaInputStream == null) {
             throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
         }
diff --git a/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANOpenProtocol.java
similarity index 89%
copy from protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
copy to protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANOpenProtocol.java
index 853fb50..e6ddd5a 100644
--- a/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
+++ b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANOpenProtocol.java
@@ -27,16 +27,16 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class CANProtocol implements Protocol {
+public class CANOpenProtocol implements Protocol {
 
     @Override
     public String getName() {
-        return "can";
+        return "canopen";
     }
 
     @Override
     public Map<String, TypeDefinition> getTypeDefinitions() throws GenerationException {
-        InputStream schemaInputStream = CANProtocol.class.getResourceAsStream("/protocols/can/can.mspec");
+        InputStream schemaInputStream = CANOpenProtocol.class.getResourceAsStream("/protocols/can/canopen.mspec");
         if(schemaInputStream == null) {
             throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
         }
diff --git a/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/SocketCANProtocol.java
similarity index 88%
rename from protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
rename to protocols/can/src/main/java/org/apache/plc4x/protocol/can/SocketCANProtocol.java
index 853fb50..dba9730 100644
--- a/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
+++ b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/SocketCANProtocol.java
@@ -27,16 +27,16 @@ import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationExcepti
 import java.io.InputStream;
 import java.util.Map;
 
-public class CANProtocol implements Protocol {
+public class SocketCANProtocol implements Protocol {
 
     @Override
     public String getName() {
-        return "can";
+        return "socketcan";
     }
 
     @Override
     public Map<String, TypeDefinition> getTypeDefinitions() throws GenerationException {
-        InputStream schemaInputStream = CANProtocol.class.getResourceAsStream("/protocols/can/can.mspec");
+        InputStream schemaInputStream = SocketCANProtocol.class.getResourceAsStream("/protocols/can/socketcan.mspec");
         if(schemaInputStream == null) {
             throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
         }
diff --git a/protocols/can/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol b/protocols/can/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
index d422eca..bf88883 100644
--- a/protocols/can/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
+++ b/protocols/can/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
@@ -16,4 +16,6 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-org.apache.plc4x.protocol.can.CANProtocol
\ No newline at end of file
+org.apache.plc4x.protocol.can.CAN20AProtocol
+org.apache.plc4x.protocol.can.SocketCANProtocol
+org.apache.plc4x.protocol.can.CANOpenProtocol
\ No newline at end of file
diff --git a/protocols/can/src/main/resources/protocols/can/can20a.mspec b/protocols/can/src/main/resources/protocols/can/can20a.mspec
new file mode 100644
index 0000000..ffa733a
--- /dev/null
+++ b/protocols/can/src/main/resources/protocols/can/can20a.mspec
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+[type 'CANFrame'
+    [simple uint 11 'identifier']
+    [simple bit 'extended']
+    [simple bit 'remote']
+    [simple bit 'error']
+]
\ No newline at end of file
diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
new file mode 100644
index 0000000..553d12a
--- /dev/null
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+[type 'CANOpenFrame'
+    [enum CANOpenService 'function']
+    [simple int 11 'identifier']
+    [reserved int 9 '0x0'] // filling gap used by extended frame, and extended marker which should always be 0
+    [simple bit 'remote']
+    [simple bit 'error']
+    [reserved int 5 '0x0']  // filling gap used by extended frame
+    [implicit uint 8 'size' 'COUNT(payload)']
+    [reserved uint 8 '0x0'] // in case of fd frame these are flags
+    [reserved uint 8 '0x0'] // padding 1
+    [reserved uint 8 '0x0'] // padding 2
+    [simple CANOpenPayload 'payload' ['function', 'size']]
+]
+
+[enum uint 4 'CANOpenService'
+    ['0b1110' NMT]
+]
+
+[discriminatedType 'CANOpenPayload' [CANOpenService 'function', uint 8 'size']
+    [typeSwitch 'function'
+        ['CANOpenService.NMT' CANOpenNetworkPayload [uint 8 'size']
+            [array int 8 'data' COUNT 'size']
+        ]
+    ]
+]
\ No newline at end of file
diff --git a/protocols/can/src/main/resources/protocols/can/can.mspec b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
similarity index 61%
rename from protocols/can/src/main/resources/protocols/can/can.mspec
rename to protocols/can/src/main/resources/protocols/can/socketcan.mspec
index 147492b..e2e9e3b 100644
--- a/protocols/can/src/main/resources/protocols/can/can.mspec
+++ b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
@@ -17,41 +17,6 @@
  * under the License.
  */
 
-[type 'CANFrame'
-    [simple CANHeader 'header']
-    [simple uint 11 'identifier']
-    [simple bit 'extended']
-    [simple bit 'remote']
-    [simple bit 'error']
-]
-
-[type 'CANHeader'
-    [simple uint 11 'identifier']
-    [simple bit 'extended']
-    [simple bit 'remote']
-    [simple bit 'error']
-
-]
-
-/* These are structures defined in linux kernel, provided here just for information purposes
-struct can_frame {
-  canid_t can_id;  // 32 bit CAN_ID + EFF/RTR/ERR flags
-  __u8    can_dlc; // frame payload length in byte (0 .. 8)
-  __u8    __pad;   // padding
-  __u8    __res0;  // reserved / padding
-  __u8    __res1;  // reserved / padding
-  __u8    data[8] __attribute__((aligned(8)));
-};
-struct canfd_frame {
-  canid_t can_id;  // 32 bit CAN_ID + EFF/RTR/ERR flags
-  __u8    len;     // frame payload length in byte (0 .. 64)
-  __u8    flags;   // additional flags for CAN FD
-  __u8    __res0;  // reserved / padding
-  __u8    __res1;  // reserved / padding
-  __u8    data[64] __attribute__((aligned(8)));
-};
-*/
-
 [type 'BrokenSocketCANFrame'
     [discriminator bit 'extended']
     [simple bit 'remote']
@@ -107,30 +72,4 @@ struct canfd_frame {
     [reserved uint 8 '0x0'] // padding 1
     [reserved uint 8 '0x0'] // padding 2
     [array int 8 'data' COUNT 'size']
-]
-
-[type 'SimplifiedSocketCANFrame'
-    [simple bit 'extended']
-    [simple bit 'remote']
-    [simple bit 'error']
-    [simple uint 29 'identifier']
-    //implicit uint 8 'size' 'COUNT(data)'
-    [reserved uint 8 '0x0'] // flags
-    [reserved uint 8 '0x0'] // padding
-    [reserved uint 8 '0x0'] // padding
-    //array int 8 'data' COUNT 'size'
-]
-
-[enum 'CanOpenNMTCommand'
-    ['0x01', START_DEVICE]
-    ['0x02', STOP_DEVICE]
-    ['0x80', PRE_START]
-    ['0x81', RESET_DEVICE]
-    ['0x82', RESET_COMMUNICATION]
-]
-
-[dataIo 'CANOpenFrame' [uint 4 'function', uint 7 nodeId, int 8 'data']
-    [discriminator uint 4 'afunction']
-    [typeSwitch 'afunction'
-    ]
 ]
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/pom.xml b/sandbox/test-java-can-driver/pom.xml
index 86bfddf..50e48e7 100644
--- a/sandbox/test-java-can-driver/pom.xml
+++ b/sandbox/test-java-can-driver/pom.xml
@@ -37,13 +37,25 @@
         <artifactId>plc4x-maven-plugin</artifactId>
         <executions>
           <execution>
-            <id>test</id>
+            <id>socketcan</id>
             <phase>generate-sources</phase>
             <goals>
               <goal>generate-driver</goal>
             </goals>
             <configuration>
-              <protocolName>can</protocolName>
+              <protocolName>socketcan</protocolName>
+              <languageName>java</languageName>
+              <outputFlavor>read-write</outputFlavor>
+            </configuration>
+          </execution>
+          <execution>
+            <id>canopen</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>generate-driver</goal>
+            </goals>
+            <configuration>
+              <protocolName>canopen</protocolName>
               <languageName>java</languageName>
               <outputFlavor>read-write</outputFlavor>
             </configuration>
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
index e1a55ed..c135280 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
@@ -24,8 +24,8 @@ import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.can.context.CANDriverContext;
 import org.apache.plc4x.java.can.field.CANFieldHandler;
 import org.apache.plc4x.java.can.protocol.CANProtocolLogic;
-import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
-import org.apache.plc4x.java.can.readwrite.io.SocketCANFrameIO;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.socketcan.readwrite.io.SocketCANFrameIO;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
new file mode 100644
index 0000000..5f6b4d3
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
@@ -0,0 +1,18 @@
+package org.apache.plc4x.java.can.helper;
+
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+
+public class CANOpenHelper {
+
+    public static CANOpenService readFunction(short identifier) {
+        return CANOpenService.valueOf((byte) (identifier >> 7));
+    }
+
+    public static void writeFunction(WriteBuffer io, short identifier) {
+        // NOOP - a placeholder to let mspec compile
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
index c0363f6..2ad7a1c 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
@@ -1,6 +1,6 @@
 package org.apache.plc4x.java.can.helper;
 
-import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANProtocolLogic.java
index 57dd683..b556a07 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANProtocolLogic.java
@@ -18,7 +18,7 @@ under the License.
 */
 package org.apache.plc4x.java.can.protocol;
 
-import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.slf4j.Logger;
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenTest.java
similarity index 52%
copy from sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
copy to sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenTest.java
index e8f08f4..a97a1eb 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenTest.java
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
+/*
   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
@@ -16,25 +15,16 @@
   KIND, either express or implied.  See the License for the
   specific language governing permissions and limitations
   under the License.
-  -->
-<test:testsuite xmlns:test="https://plc4x.apache.org/schemas/parser-serializer-testsuite.xsd">
+*/
 
-  <name>Tests of socketcan/CAN 2.0B frames</name>
+package org.apache.plc4x.java.can;
 
-  <testcase>
-    <name>Extended frame 1E6EC676#05.05.1F.26.C3</name>
-    <raw>76c66e9e0500000005051f26c3</raw>
-    <root-type>SocketCANFrame</root-type>
-    <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
-        <rawId>-1636907402</rawId>
-        <data>BQUfJsM=</data>
-        <identifier>510576246</identifier>
-        <extended>true</extended>
-        <remote>false</remote>
-        <error>false</error>
-      </SocketCANFrame>
-    </xml>
-  </testcase>
+import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
 
-</test:testsuite>
\ No newline at end of file
+public class CANOpenTest extends ParserSerializerTestsuiteRunner {
+
+    public CANOpenTest() {
+        super("/testsuite/CANOpenTestSuite.xml");
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenTestSuite.xml
similarity index 71%
copy from sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
copy to sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenTestSuite.xml
index e8f08f4..b99128d 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenTestSuite.xml
@@ -19,20 +19,20 @@
   -->
 <test:testsuite xmlns:test="https://plc4x.apache.org/schemas/parser-serializer-testsuite.xsd">
 
-  <name>Tests of socketcan/CAN 2.0B frames</name>
+  <name>Tests of socketcan/CANopen frames.</name>
 
   <testcase>
-    <name>Extended frame 1E6EC676#05.05.1F.26.C3</name>
-    <raw>76c66e9e0500000005051f26c3</raw>
-    <root-type>SocketCANFrame</root-type>
+    <name>Network heartbeat frame: 728#05</name>
+    <raw>280700000100000005</raw>
+    <root-type>CANOpenFrame</root-type>
     <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
-        <rawId>-1636907402</rawId>
-        <data>BQUfJsM=</data>
-        <identifier>510576246</identifier>
-        <extended>true</extended>
+      <SocketCANFrame className="org.apache.plc4x.java.canopen.readwrite.CANOpenFrame">
+        <function>15</function>
+        <identifier>5</identifier>
+        <extended>false</extended>
         <remote>false</remote>
         <error>false</error>
+        <data>ESIzRFVmd4g=</data>
       </SocketCANFrame>
     </xml>
   </testcase>
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20ATestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20ATestSuite.xml
index 60140de..3e647c5 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20ATestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20ATestSuite.xml
@@ -26,7 +26,7 @@
     <raw>a1050000080000001122334455667788</raw>
     <root-type>SocketCAN20AFrame</root-type>
     <xml>
-      <SocketCAN20AFrame className="org.apache.plc4x.java.can.readwrite.SocketCAN20AFrame">
+      <SocketCAN20AFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCAN20AFrame">
         <identifier>1441</identifier>
         <extended>false</extended>
         <remote>false</remote>
@@ -41,7 +41,7 @@
     <raw>a105000000000000</raw>
     <root-type>SocketCAN20AFrame</root-type>
     <xml>
-      <SocketCAN20AFrame className="org.apache.plc4x.java.can.readwrite.SocketCAN20AFrame">
+      <SocketCAN20AFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCAN20AFrame">
         <identifier>1441</identifier>
         <extended>false</extended>
         <remote>false</remote>
@@ -56,7 +56,7 @@
     <raw>a105004000000000</raw>
     <root-type>SocketCAN20AFrame</root-type>
     <xml>
-      <SocketCAN20AFrame className="org.apache.plc4x.java.can.readwrite.SocketCAN20AFrame">
+      <SocketCAN20AFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCAN20AFrame">
         <identifier>1441</identifier>
         <extended>false</extended>
         <remote>true</remote>
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
index e8f08f4..0e9caf3 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCAN20BTestSuite.xml
@@ -26,7 +26,7 @@
     <raw>76c66e9e0500000005051f26c3</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+      <SocketCANFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame">
         <rawId>-1636907402</rawId>
         <data>BQUfJsM=</data>
         <identifier>510576246</identifier>
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
index f2518c0..03eaaf1 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
@@ -26,7 +26,7 @@
     <raw>a1050000080000001122334455667788</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+      <SocketCANFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame">
         <rawId>1441</rawId>
         <data>ESIzRFVmd4g=</data>
         <identifier>1441</identifier>
@@ -42,7 +42,7 @@
     <raw>a105000000000000</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+      <SocketCANFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame">
         <rawId>1441</rawId>
         <data></data>
         <identifier>1441</identifier>
@@ -58,7 +58,7 @@
     <raw>a105004000000000</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.can.readwrite.SocketCANFrame">
+      <SocketCANFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame">
         <rawId>1073743265</rawId>
         <data></data>
         <identifier>1441</identifier>


[plc4x] 01/21: Scratch of socketcan transport and can frame definitions.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 76e5724da88b5fdacc9509c1004289249193bf58
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Tue Aug 18 00:40:25 2020 +0200

    Scratch of socketcan transport and can frame definitions.
---
 plc4j/transports/pom.xml                           |   1 +
 plc4j/transports/socketcan/pom.xml                 |  81 +++++++
 .../socketcan/CANTransportConfiguration.java       |  25 ++
 .../socketcan/SocketCANChannelFactory.java         |  70 ++++++
 .../transport/socketcan/SocketCANTransport.java    |  46 ++++
 .../socketcan/netty/SocketCANChannel.java          | 267 +++++++++++++++++++++
 .../socketcan/netty/SocketCANChannelConfig.java    |  31 +++
 .../socketcan/netty/address/SocketCANAddress.java  |  40 +++
 .../org.apache.plc4x.java.spi.transport.Transport  |  20 ++
 protocols/can/pom.xml                              |  43 ++++
 .../org/apache/plc4x/protocol/can/CANProtocol.java |  46 ++++
 ...e.plc4x.plugins.codegenerator.protocol.Protocol |  19 ++
 .../can/src/main/resources/protocols/can/can.mspec |  77 ++++++
 protocols/pom.xml                                  |   1 +
 sandbox/pom.xml                                    |   1 +
 sandbox/test-java-can-driver/pom.xml               | 133 ++++++++++
 .../org/apache/plc4x/java/can/CANPlcDriver.java    | 102 ++++++++
 .../java/can/configuration/CANConfiguration.java   |  26 ++
 .../plc4x/java/can/context/CANDriverContext.java   |  24 ++
 .../plc4x/java/can/field/CANFieldHandler.java      |  33 +++
 .../plc4x/java/can/protocol/CANProtocolLogic.java  |  51 ++++
 .../services/org.apache.plc4x.java.api.PlcDriver   |  19 ++
 .../test/java/org/apache/plc4x/java/can/Main.java  |  36 +++
 23 files changed, 1192 insertions(+)

diff --git a/plc4j/transports/pom.xml b/plc4j/transports/pom.xml
index 644bf4d..a07d9a3 100644
--- a/plc4j/transports/pom.xml
+++ b/plc4j/transports/pom.xml
@@ -37,6 +37,7 @@
     <module>pcap-replay</module>
     <module>pcap-shared</module>
     <module>raw-socket</module>
+    <module>socketcan</module>
     <module>serial</module>
     <module>tcp</module>
     <module>udp</module>
diff --git a/plc4j/transports/socketcan/pom.xml b/plc4j/transports/socketcan/pom.xml
new file mode 100644
index 0000000..0e28c7b
--- /dev/null
+++ b/plc4j/transports/socketcan/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.plc4x</groupId>
+    <artifactId>plc4j-transports</artifactId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>plc4j-transport-socketcan</artifactId>
+  <name>PLC4J: Transports: SocketCAN</name>
+  <description>Base classes needed to implement plc4x drivers based on SocketCAN connections.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-spi</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>tel.schich</groupId>
+      <artifactId>javacan</artifactId>
+      <version>2.1.0</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-transport</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!--
+       Generate an OSGI compatible MANIFEST file.
+     -->
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+            <Bundle-Activator>org.apache.plc4x.java.osgi.TransportActivator</Bundle-Activator>
+            <Export-Service>org.apache.plc4x.java.spi.transport.Transport,org.apache.plc4x.java.transport.socketcan.SocketCANTransport</Export-Service>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file
diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/CANTransportConfiguration.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/CANTransportConfiguration.java
new file mode 100644
index 0000000..791b465
--- /dev/null
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/CANTransportConfiguration.java
@@ -0,0 +1,25 @@
+/*
+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.transport.socketcan;
+
+import org.apache.plc4x.java.spi.transport.TransportConfiguration;
+
+public interface CANTransportConfiguration extends TransportConfiguration {
+
+}
diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANChannelFactory.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANChannelFactory.java
new file mode 100644
index 0000000..f4c7cc7
--- /dev/null
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANChannelFactory.java
@@ -0,0 +1,70 @@
+/*
+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.transport.socketcan;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.ThreadPerChannelEventLoop;
+import io.netty.channel.oio.OioEventLoopGroup;
+import org.apache.plc4x.java.spi.configuration.HasConfiguration;
+import org.apache.plc4x.java.spi.connection.NettyChannelFactory;
+import org.apache.plc4x.java.transport.socketcan.netty.SocketCANChannel;
+import org.apache.plc4x.java.transport.socketcan.netty.address.SocketCANAddress;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SocketCANChannelFactory extends NettyChannelFactory implements HasConfiguration<CANTransportConfiguration> {
+
+    private static final Logger logger = LoggerFactory.getLogger(SocketCANChannelFactory.class);
+
+    private CANTransportConfiguration configuration;
+
+    public SocketCANChannelFactory(SocketCANAddress address) {
+        super(address);
+    }
+
+    @Override
+    public void setConfiguration(CANTransportConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    public Class<? extends Channel> getChannel() {
+        return SocketCANChannel.class;
+    }
+
+    @Override
+    public boolean isPassive() {
+        return true;
+    }
+
+    @Override
+    public EventLoopGroup getEventLoopGroup() {
+        return new ThreadPerChannelEventLoop(new OioEventLoopGroup());
+    }
+
+    @Override
+    public void configureBootstrap(Bootstrap bootstrap) {
+        if(configuration != null) {
+            logger.info("Configuring Bootstrap with {}", configuration);
+        }
+    }
+
+}
diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANTransport.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANTransport.java
new file mode 100644
index 0000000..95279c1
--- /dev/null
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANTransport.java
@@ -0,0 +1,46 @@
+/*
+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.transport.socketcan;
+
+import org.apache.plc4x.java.spi.connection.ChannelFactory;
+import org.apache.plc4x.java.spi.transport.Transport;
+import org.apache.plc4x.java.transport.socketcan.netty.address.SocketCANAddress;
+
+/**
+ * CAN specific transport which rely on JavaCAN library.
+ */
+public class SocketCANTransport implements Transport {
+
+    @Override
+    public String getTransportCode() {
+        return "javacan";
+    }
+
+    @Override
+    public String getTransportName() {
+        return "JavaCAN Transport";
+    }
+
+    @Override
+    public ChannelFactory createChannelFactory(String transportConfig) {
+        SocketCANAddress address = new SocketCANAddress(transportConfig);
+        return new SocketCANChannelFactory(address);
+    }
+
+}
diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
new file mode 100644
index 0000000..764d1f5
--- /dev/null
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
@@ -0,0 +1,267 @@
+/*
+ * 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.transport.socketcan.netty;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelConfig;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.oio.OioByteStreamChannel;
+import org.apache.plc4x.java.transport.socketcan.netty.address.SocketCANAddress;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tel.schich.javacan.CanChannels;
+import tel.schich.javacan.CanFrame;
+import tel.schich.javacan.NetworkDevice;
+import tel.schich.javacan.RawCanChannel;
+import tel.schich.javacan.linux.LinuxNetworkDevice;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+
+/**
+ * A naive copy of pcap channel implementation which allows to pass over data to PLC4X as well as drop it back to JavaCAN.
+ * Sadly all this involves double parsing.
+ */
+public class SocketCANChannel extends OioByteStreamChannel {
+
+    private static final Logger logger = LoggerFactory.getLogger(SocketCANChannel.class);
+
+    private final SocketCANChannelConfig config;
+
+    private SocketCANAddress remoteRawSocketAddress;
+    private SocketAddress localAddress;
+    private RawCanChannel handle;
+    private Thread loopThread;
+
+    public SocketCANChannel() {
+        super(null);
+        config = new SocketCANChannelConfig(this);
+    }
+
+    @Override
+    protected boolean isInputShutdown() {
+        return false;
+    }
+
+    @Override
+    protected ChannelFuture shutdownInput() {
+        throw new UnsupportedOperationException("");
+    }
+
+    @Override
+    protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
+        if (!(remoteAddress instanceof SocketCANAddress)) {
+            logger.error("Expecting remote address of type SocketCANAddress");
+            pipeline().fireExceptionCaught(new IllegalArgumentException("Expecting remote address of type SocketCANAddress"));
+            return;
+        }
+        this.localAddress = localAddress;
+        remoteRawSocketAddress = (SocketCANAddress) remoteAddress;
+
+        // Try to get the device name of the network interface that we want to open.
+        String interfaceName = getInterfaceName(remoteRawSocketAddress);
+        if(interfaceName == null) {
+            logger.error("Interface name is not specified and couldn't detect it automatically");
+            pipeline().fireExceptionCaught(new IllegalArgumentException("Interface name is not specified and couldn't detect it automatically"));
+            return;
+        }
+
+        NetworkDevice device = LinuxNetworkDevice.lookup(interfaceName);
+        // Get a handle to the network-device and open it.
+        handle = CanChannels.newRawChannel(device);
+
+        if(logger.isDebugEnabled()) {
+            logger.debug(String.format("Listening on device %s", interfaceName));
+        }
+
+        // TODO If the address allows fine tuning which packets to process, set a filter to reduce the load.
+//        String filter =
+//        if(filter.length() > 0) {
+//            handle.setFilter(filter, BpfProgram.BpfCompileMode.OPTIMIZE);
+//        }
+
+        // Create a buffer where the raw socket worker can send data to.
+        ByteBuf buffer = Unpooled.buffer();
+
+        // Start a thread that processes the callbacks from the raw socket and simply
+        // forwards the bytes read to the buffer.
+        loopThread = new Thread(() -> {
+            try {
+                while (!isInputShutdown()) {
+                    CanFrame frame = handle.read();
+                    ByteBuf frameBytes = ByteBufAllocator.DEFAULT.buffer();
+                    frameBytes.writeBytes(frame.getBuffer());
+                    String dump = ByteBufUtil.prettyHexDump(frameBytes);
+                    System.out.println(frame + "\n" + dump);
+                    buffer.writeBytes(frame.getBuffer());
+                }
+            } catch (IOException e) {
+                logger.warn("Could not read data", e);
+                pipeline().fireExceptionCaught(e);
+            }
+        });
+        loopThread.start();
+
+        activate(new CANInputStream(buffer), new CANOutputStream(handle));
+    }
+
+    @Override
+    protected SocketAddress localAddress0() {
+        return localAddress;
+    }
+
+    @Override
+    protected SocketAddress remoteAddress0() {
+        return remoteRawSocketAddress;
+    }
+
+    @Override
+    protected void doBind(SocketAddress localAddress) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    protected void doDisconnect() {
+        this.loopThread.interrupt();
+        if (this.handle != null) {
+            try {
+                this.handle.close();
+            } catch (IOException e) {
+                logger.error("Failed to close CAN socket!");
+            }
+        }
+    }
+
+    @Override
+    protected int doReadBytes(ByteBuf buf) throws Exception {
+        if (handle == null || !handle.isOpen()) {
+            return -1;
+        }
+        try {
+            return super.doReadBytes(buf);
+        } catch (SocketTimeoutException ignored) {
+            return 0;
+        }
+    }
+
+    @Override
+    public ChannelConfig config() {
+        return this.config;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return true;
+    }
+
+    @Override
+    protected AbstractUnsafe newUnsafe() {
+        return new RawSocketUnsafe();
+    }
+
+    private String getInterfaceName(SocketCANAddress address) {
+        // If the device name is provided, simply use this.
+        if(address.getInterfaceName() != null) {
+            return address.getInterfaceName();
+        }
+
+        // TODO: Implement this ...
+        return null;
+    }
+
+    /**
+     * InputStream that fulfills the contract of Netty for read operations to timeout.
+     * Without this the InputStream would simply block indefinitely which would block
+     * the entire IO module.
+     */
+    private static class CANInputStream extends InputStream {
+        final ByteBuf buf;
+
+        private CANInputStream(ByteBuf buf) {
+            this.buf = buf;
+        }
+
+        @Override
+        public int available() {
+            return buf.readableBytes();
+        }
+
+        @Override
+        public int read() throws IOException {
+            // Timeout 10 ms
+            final long timeout = System.nanoTime() + 10_000;
+            // Yes, this could make the thread go nuts in case of no data,
+            // but the Netty guys are doing it the same way and there probably
+            // is a reason for it ;-)
+            while (System.nanoTime() < timeout) {
+                if (buf.readableBytes() > 0) {
+                    return buf.readByte() & 0xFF;
+                }
+            }
+            throw new SocketTimeoutException();
+        }
+    }
+
+
+    private static class CANOutputStream extends OutputStream {
+
+        private final RawCanChannel rawCanChannel;
+
+        public CANOutputStream(RawCanChannel rawCanChannel) {
+            this.rawCanChannel = rawCanChannel;
+        }
+
+        @Override
+        public void write(int b) throws IOException {
+            throw new IOException("Appending single bytes is not permitted. Use write(byte[], int, int)");
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+            ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
+            CanFrame frame = CanFrame.create(buffer);
+            rawCanChannel.write(frame);
+        }
+    }
+
+    /**
+     * Internal helper to wrap access to unsafe operations (Only used internally by netty)
+     */
+    private class RawSocketUnsafe extends AbstractUnsafe {
+        @Override
+        public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
+            try {
+                doConnect(remoteAddress, localAddress);
+                pipeline().fireChannelActive();
+                promise.setSuccess();
+            } catch (Exception e) {
+                promise.setFailure(e);
+            }
+        }
+    }
+}
diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannelConfig.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannelConfig.java
new file mode 100644
index 0000000..42f604a
--- /dev/null
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannelConfig.java
@@ -0,0 +1,31 @@
+/*
+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.transport.socketcan.netty;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelConfig;
+import io.netty.channel.DefaultChannelConfig;
+
+public class SocketCANChannelConfig extends DefaultChannelConfig implements ChannelConfig {
+
+    public SocketCANChannelConfig(Channel channel) {
+        super(channel);
+    }
+
+}
diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/address/SocketCANAddress.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/address/SocketCANAddress.java
new file mode 100644
index 0000000..e11ff17
--- /dev/null
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/address/SocketCANAddress.java
@@ -0,0 +1,40 @@
+/*
+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.transport.socketcan.netty.address;
+
+import java.net.SocketAddress;
+
+/**
+ * Simple address implementation which covers only interface name.
+ */
+public class SocketCANAddress extends SocketAddress {
+
+    private static final long serialVersionUID = 1L;
+
+    private final String interfaceName;
+
+    public SocketCANAddress(String interfaceName) {
+        this.interfaceName = interfaceName;
+    }
+
+    public String getInterfaceName() {
+        return interfaceName;
+    }
+
+}
diff --git a/plc4j/transports/socketcan/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.transport.Transport b/plc4j/transports/socketcan/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.transport.Transport
new file mode 100644
index 0000000..31b1a03
--- /dev/null
+++ b/plc4j/transports/socketcan/src/main/resources/META-INF/services/org.apache.plc4x.java.spi.transport.Transport
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+org.apache.plc4x.java.transport.socketcan.SocketCANTransport
\ No newline at end of file
diff --git a/protocols/can/pom.xml b/protocols/can/pom.xml
new file mode 100644
index 0000000..be98554
--- /dev/null
+++ b/protocols/can/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.plc4x</groupId>
+    <artifactId>plc4x-protocols</artifactId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>plc4x-protocols-can</artifactId>
+
+  <name>Protocols: CAN</name>
+  <description>CAN stands for Controller Area Network.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-build-utils-protocol-base-mspec</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
new file mode 100644
index 0000000..853fb50
--- /dev/null
+++ b/protocols/can/src/main/java/org/apache/plc4x/protocol/can/CANProtocol.java
@@ -0,0 +1,46 @@
+/*
+ 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.protocol.can;
+
+import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
+import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
+import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
+
+import java.io.InputStream;
+import java.util.Map;
+
+public class CANProtocol implements Protocol {
+
+    @Override
+    public String getName() {
+        return "can";
+    }
+
+    @Override
+    public Map<String, TypeDefinition> getTypeDefinitions() throws GenerationException {
+        InputStream schemaInputStream = CANProtocol.class.getResourceAsStream("/protocols/can/can.mspec");
+        if(schemaInputStream == null) {
+            throw new GenerationException("Error loading message-format schema for protocol '" + getName() + "'");
+        }
+        return new MessageFormatParser().parse(schemaInputStream);
+    }
+
+}
diff --git a/protocols/can/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol b/protocols/can/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
new file mode 100644
index 0000000..d422eca
--- /dev/null
+++ b/protocols/can/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+org.apache.plc4x.protocol.can.CANProtocol
\ No newline at end of file
diff --git a/protocols/can/src/main/resources/protocols/can/can.mspec b/protocols/can/src/main/resources/protocols/can/can.mspec
new file mode 100644
index 0000000..bbee418
--- /dev/null
+++ b/protocols/can/src/main/resources/protocols/can/can.mspec
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+[type 'CANFrame'
+    [simple uint 11 'identifier']
+    [simple bit 'remoteTransmissionRequest']
+    [discriminator bit 'extended']
+    [typeSwitch 'extended'
+        ['false' CANDataFrame
+            [reserved uint 1 '0x0']
+        ]
+        ['true' ExtendedCANFrame
+            [simple uint 18 'extensionId']
+            [simple bit 'extensionRemoteTransmissionRequest']
+            [reserved uint 2 '0x0']
+        ]
+    ]
+    [simple uint 4 'length']
+    [array uint 8 'data' count 'length']
+    [simple uint 15 'crc']
+]
+
+/* These are structures defined in linux kernel, provided here just for information purposes
+struct can_frame {
+  canid_t can_id;  // 32 bit CAN_ID + EFF/RTR/ERR flags
+  __u8    can_dlc; // frame payload length in byte (0 .. 8)
+  __u8    __pad;   // padding
+  __u8    __res0;  // reserved / padding
+  __u8    __res1;  // reserved / padding
+  __u8    data[8] __attribute__((aligned(8)));
+};
+struct canfd_frame {
+  canid_t can_id;  // 32 bit CAN_ID + EFF/RTR/ERR flags
+  __u8    len;     // frame payload length in byte (0 .. 64)
+  __u8    flags;   // additional flags for CAN FD
+  __u8    __res0;  // reserved / padding
+  __u8    __res1;  // reserved / padding
+  __u8    data[64] __attribute__((aligned(8)));
+};
+*/
+
+[type 'SocketCANFrame'
+    [simple uint 29 'identifier']
+    [simple bit 'extended']
+    [simple bit 'remote']
+    [simple bit 'error']
+    [implicit uint 8 'length' 'ARRAY_SIZE_IN_BYTES(data)']
+    [typeSwitch 'extended', 'identifier'
+        ['true' SocketCANFDFrame
+            [simple uint 8 'flags']
+            [reserved uint 8 '0x0']
+            [reserved uint 8 '0x0']
+        ]
+        ['false' ScoketCANFrame
+            [reserved uint 8 '0x0']
+            [reserved uint 8 '0x0']
+            [reserved uint 8 '0x0']
+        ]
+    ]
+    [array int 8 'data' COUNT 'length']
+]
diff --git a/protocols/pom.xml b/protocols/pom.xml
index 1459800..2191ba9 100644
--- a/protocols/pom.xml
+++ b/protocols/pom.xml
@@ -43,6 +43,7 @@
     <module>knxnetip</module>
     <module>modbus</module>
     <module>s7</module>
+    <module>can</module>
   </modules>
 
   <build>
diff --git a/sandbox/pom.xml b/sandbox/pom.xml
index 27e3b8d..26c5f83 100644
--- a/sandbox/pom.xml
+++ b/sandbox/pom.xml
@@ -42,6 +42,7 @@
 
     <module>test-java-bacnetip-driver</module>
     <module>test-java-df1-driver</module>
+    <module>test-java-can-driver</module>
 
     <module>test-streampipes-plc4x-adapters</module>
     <module>test-streampipes-plc4x-processors</module>
diff --git a/sandbox/test-java-can-driver/pom.xml b/sandbox/test-java-can-driver/pom.xml
new file mode 100644
index 0000000..86bfddf
--- /dev/null
+++ b/sandbox/test-java-can-driver/pom.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.plc4x.sandbox</groupId>
+    <artifactId>plc4x-sandbox</artifactId>
+    <version>0.8.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>test-java-can-driver</artifactId>
+
+  <name>Sandbox: Test Generated CAN Driver</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.plc4x.plugins</groupId>
+        <artifactId>plc4x-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>test</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>generate-driver</goal>
+            </goals>
+            <configuration>
+              <protocolName>can</protocolName>
+              <languageName>java</languageName>
+              <outputFlavor>read-write</outputFlavor>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-api</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-transport-socketcan</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.cmpn</artifactId>
+      <version>6.0.0</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-build-utils-language-java</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+      <!-- Scope is 'provided' as this way it's not shipped with the driver -->
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- Logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>log4j-over-slf4j</artifactId>
+      <version>1.7.25</version>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4x-protocols-can</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+      <!-- Scope is 'provided' as this way it's not shipped with the driver -->
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.plc4x</groupId>
+      <artifactId>plc4j-utils-test-utils</artifactId>
+      <version>0.8.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.dataformat</groupId>
+      <artifactId>jackson-dataformat-xml</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
new file mode 100644
index 0000000..4b7eb16
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANPlcDriver.java
@@ -0,0 +1,102 @@
+/*
+ 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.can;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.can.configuration.CANConfiguration;
+import org.apache.plc4x.java.can.context.CANDriverContext;
+import org.apache.plc4x.java.can.field.CANFieldHandler;
+import org.apache.plc4x.java.can.protocol.CANProtocolLogic;
+import org.apache.plc4x.java.can.readwrite.CANFrame;
+import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.can.readwrite.io.CANFrameIO;
+import org.apache.plc4x.java.can.readwrite.io.SocketCANFrameIO;
+import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
+import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
+import tel.schich.javacan.CanFrame;
+import tel.schich.javacan.CanId;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+
+/**
+ */
+public class CANPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
+
+    @Override
+    public String getProtocolCode() {
+        return "can";
+    }
+
+    @Override
+    public String getProtocolName() {
+        return "Controller Area Network";
+    }
+
+    @Override
+    protected Class<? extends Configuration> getConfigurationType() {
+        return CANConfiguration.class;
+    }
+
+    @Override
+    protected String getDefaultTransport() {
+        return "javacan";
+    }
+
+    @Override
+    protected CANFieldHandler getFieldHandler() {
+        return new CANFieldHandler();
+    }
+
+    @Override
+    protected ProtocolStackConfigurer<SocketCANFrame> getStackConfigurer() {
+        return SingleProtocolStackConfigurer.builder(SocketCANFrame.class, SocketCANFrameIO.class)
+            .withProtocol(CANProtocolLogic.class)
+            .withDriverContext(CANDriverContext.class)
+            .withPacketSizeEstimator(CANEstimator.class)
+            //.withCorruptPacketRemover(CANCleaner.class)
+            .build();
+    }
+
+    public static class CANEstimator implements ToIntFunction<ByteBuf> {
+        @Override
+        public int applyAsInt(ByteBuf byteBuf) {
+            if (byteBuf.readableBytes() >= 5) {
+                System.out.println(ByteBufUtil.prettyHexDump(byteBuf));
+                byte len = byteBuf.getByte(4);
+                System.out.println("Length " + (int) len);
+                return len + 8 /* overhead */;
+            }
+            return -1; //discard
+        }
+    }
+
+    public class CANCleaner implements Consumer<ByteBuf> {
+        @Override
+        public void accept(ByteBuf byteBuf) {
+            System.out.println("Discard");
+            byteBuf.readByte();
+        }
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
new file mode 100644
index 0000000..1a86e45
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
@@ -0,0 +1,26 @@
+/*
+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.can.configuration;
+
+import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.transport.socketcan.CANTransportConfiguration;
+
+public class CANConfiguration implements Configuration, CANTransportConfiguration {
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/context/CANDriverContext.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/context/CANDriverContext.java
new file mode 100644
index 0000000..4f0d553
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/context/CANDriverContext.java
@@ -0,0 +1,24 @@
+/*
+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.can.context;
+
+import org.apache.plc4x.java.spi.context.DriverContext;
+
+public class CANDriverContext implements DriverContext {
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANFieldHandler.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANFieldHandler.java
new file mode 100644
index 0000000..2f2c3f8
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANFieldHandler.java
@@ -0,0 +1,33 @@
+/*
+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.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.spi.connection.DefaultPlcFieldHandler;
+import org.apache.plc4x.java.spi.connection.PlcFieldHandler;
+
+public class CANFieldHandler extends DefaultPlcFieldHandler implements PlcFieldHandler {
+
+    @Override
+    public PlcField createField(String fieldQuery) throws PlcInvalidFieldException {
+        return null;
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANProtocolLogic.java
new file mode 100644
index 0000000..57dd683
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANProtocolLogic.java
@@ -0,0 +1,51 @@
+/*
+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.can.protocol;
+
+import org.apache.plc4x.java.can.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CANProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> {
+
+    private Logger logger = LoggerFactory.getLogger(CANProtocolLogic.class);
+
+    @Override
+    public void onConnect(ConversationContext<SocketCANFrame> context) {
+        context.fireConnected();
+    }
+
+    @Override
+    protected void decode(ConversationContext<SocketCANFrame> context, SocketCANFrame msg) throws Exception {
+        logger.info("Decode CAN message {}", msg);
+    }
+
+    @Override
+    public void close(ConversationContext<SocketCANFrame> context) {
+
+    }
+
+    @Override
+    public void onDisconnect(ConversationContext<SocketCANFrame> context) {
+
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver b/sandbox/test-java-can-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
new file mode 100644
index 0000000..cb3d2ec
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+org.apache.plc4x.java.can.CANPlcDriver
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
new file mode 100644
index 0000000..622f602
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
@@ -0,0 +1,36 @@
+/*
+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.can;
+
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcConnection;
+
+/**
+ * Here we begin .. ;-)
+ */
+public class Main {
+
+    public static void main(String[] args) throws Exception {
+        PlcDriverManager driverManager = new PlcDriverManager();
+
+        PlcConnection connection = driverManager.getConnection("can:javacan://vcan0");
+
+    }
+
+}


[plc4x] 16/21: CANopen milestone - support for segmentet SDO upload (read) requests.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit a1ec214c3eeaa717029482cb14b45b5c234c1d0f
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Tue Oct 6 10:58:09 2020 +0200

    CANopen milestone - support for segmentet SDO upload (read) requests.
---
 .../src/main/resources/protocols/can/canopen.mspec |  40 ++++--
 .../apache/plc4x/java/can/CANOpenPlcDriver.java    |  24 ++--
 .../org/apache/plc4x/java/can/api/CANFrame.java    |  11 ++
 .../apache/plc4x/java/can/api/CANOpenFrame.java    |  66 +++++++++
 .../api/conversation/canopen/CANConversation.java  |  18 +++
 .../api/conversation/canopen/CANFrameBuilder.java  |  11 ++
 .../conversation/canopen/CANOpenConversation.java  |  75 +++++++++++
 .../canopen/CANOpenConversationBase.java           |  15 +++
 .../api/conversation/canopen/SDOConversation.java  |  43 ++++++
 .../canopen/SDODownloadConversation.java           |  97 ++++++++++++++
 .../canopen/SDOUploadConversation.java             |  98 ++++++++++++++
 .../api/segmentation/PlcSegmentationException.java |  21 +++
 .../java/can/api/segmentation/Segmentation.java    | 131 ++++++++++++++++++
 .../api/segmentation/accumulator/ByteStorage.java  |  59 ++++++++
 .../can/api/segmentation/accumulator/Storage.java} |  40 ++++--
 .../java/can/configuration/CANConfiguration.java   |   4 +-
 .../java/can/context/CANOpenDriverContext.java     |   9 ++
 .../apache/plc4x/java/can/field/CANOpenField.java  |  51 +++++++
 .../plc4x/java/can/field/CANOpenFieldHandler.java  |  58 ++++++++
 .../plc4x/java/can/field/CANOpenNMTField.java      |  58 ++++++++
 .../plc4x/java/can/field/CANOpenSDOField.java      |  89 ++++++++++++
 .../plc4x/java/can/helper/CANOpenHelper.java       |  14 ++
 .../apache/plc4x/java/can/helper/HeaderParser.java |  13 +-
 .../apache/plc4x/java/can/listener/Callback.java   |   8 ++
 .../plc4x/java/can/listener/CompositeCallback.java |  25 ++++
 .../java/can/protocol/CANOpenProtocolLogic.java    | 149 +++++++++++++++++++--
 .../protocol/segmentation/CANOpenSegmentation.java |  86 ++++++++++++
 .../java/can/socketcan/SocketCANConversation.java  |  49 +++++++
 .../java/can/socketcan/SocketCANDelegateFrame.java |  43 ++++++
 .../java/can/socketcan/SocketCANFrameBuilder.java  |  29 ++++
 .../test/java/org/apache/plc4x/java/can/Main.java  |  48 +++++++
 .../{Main.java => field/CANOpenFieldSDOTest.java}  |  24 ++--
 .../plc4x/java/can/field/CANOpenNMTFieldTest.java} |  39 +++---
 33 files changed, 1467 insertions(+), 78 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index e5e0fe1..3b1e49d 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -22,17 +22,17 @@
     ['0b0001' SYNC            ['0x80',  '0x80' , 'false' ] ]
     ['0b0001' EMCY            ['0x81',  '0xFF' , 'false' ] ]
     ['0b0010' TIME            ['0x100', '0x100', 'false' ] ]
-    ['0b0011' TRANSMIT_PDO_1  ['0x181', '0x1FF', 'true'  ] ]
-    ['0b0100' RECEIVE_PDO_1   ['0x201', '0x27F', 'true'  ] ]
-    ['0b0101' TRANSMIT_PDO_2  ['0x281', '0x2FF', 'true'  ] ]
-    ['0b0110' RECEIVE_PDO_2   ['0x301', '0x37F', 'true'  ] ]
-    ['0b0111' TRANSMIT_PDO_3  ['0x381', '0x3FF', 'true'  ] ]
-    ['0b1000' RECEIVE_PDO_3   ['0x401', '0x47F', 'true'  ] ]
-    ['0b1001' TRANSMIT_PDO_4  ['0x481', '0x4FF', 'true'  ] ]
-    ['0b1010' RECEIVE_PDO_4   ['0x501', '0x57F', 'true'  ] ]
-    ['0b1011' TRANSMIT_SDO    ['0x581', '0x5FF', 'false' ] ]
-    ['0b1100' RECEIVE_SDO     ['0x601', '0x67F', 'false' ] ]
-    ['0b1110' HEARTBEAT       ['0x701', '0x77F', 'false' ] ]
+    ['0b0011' TRANSMIT_PDO_1  ['0x180', '0x1FF', 'true'  ] ]
+    ['0b0100' RECEIVE_PDO_1   ['0x200', '0x27F', 'true'  ] ]
+    ['0b0101' TRANSMIT_PDO_2  ['0x280', '0x2FF', 'true'  ] ]
+    ['0b0110' RECEIVE_PDO_2   ['0x300', '0x37F', 'true'  ] ]
+    ['0b0111' TRANSMIT_PDO_3  ['0x380', '0x3FF', 'true'  ] ]
+    ['0b1000' RECEIVE_PDO_3   ['0x400', '0x47F', 'true'  ] ]
+    ['0b1001' TRANSMIT_PDO_4  ['0x480', '0x4FF', 'true'  ] ]
+    ['0b1010' RECEIVE_PDO_4   ['0x500', '0x57F', 'true'  ] ]
+    ['0b1011' TRANSMIT_SDO    ['0x580', '0x5FF', 'false' ] ]
+    ['0b1100' RECEIVE_SDO     ['0x600', '0x67F', 'false' ] ]
+    ['0b1110' HEARTBEAT       ['0x700', '0x77F', 'false' ] ]
 ]
 
 [enum uint 8 'NMTStateRequest'
@@ -262,6 +262,7 @@
     [REAL64      ['64'] ]
 
     // compound/complex types
+    [RECORD           [ '8'] ]
     [OCTET_STRING     [ '8'] ]
     [VISIBLE_STRING   [ '8'] ]
     [UNICODE_STRING   ['16'] ]
@@ -269,7 +270,7 @@
     [TIME_DIFFERENCE  ['48'] ]
 ]
 
-[dataIo 'DataItem' [CANOpenDataType 'dataType']
+[dataIo 'DataItem' [CANOpenDataType 'dataType', int 32 'size']
     [typeSwitch 'dataType'
         ['CANOpenDataType.BOOLEAN' Boolean
             [simple bit 'value']
@@ -328,13 +329,28 @@
         ['CANOpenDataType.REAL64' Double
             [simple float 11.52 'value']
         ]
+        ['CANOpenDataType.RECORD' List
+            [array int 8 'value' length 'size']
+        ]
         ['CANOpenDataType.OCTET_STRING' String
+            [manual string 'UTF-8' 'value'
+                'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.parseString", io, size, _type.encoding)'
+                'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.serializeString", io, _value, _type.encoding)' '_value.length'
+            ]
         ]
         ['CANOpenDataType.VISIBLE_STRING' String
+            [manual string 'UTF-8' 'value'
+                'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.parseString", io, size, _type.encoding)'
+                'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.serializeString", io, _value, _type.encoding)' '_value.length'
+            ]
         ]
         //CANOpenDataType.TIME_OF_DAY' CANOpenTime
         //CANOpenDataType.TIME_DIFFERENCE' CANOpenTime
         ['CANOpenDataType.UNICODE_STRING' String
+            [manual string 'UTF-8' 'value'
+                'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.parseString", io, size, _type.encoding)'
+                'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.serializeString", io, _value, _type.encoding)' '_value.length'
+            ]
         ]
     ]
 ]
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
index 6acff67..a8a5f7a 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
@@ -19,21 +19,18 @@
 package org.apache.plc4x.java.can;
 
 import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
-import org.apache.plc4x.java.can.context.CANDriverContext;
+import org.apache.plc4x.java.can.context.CANOpenDriverContext;
 import org.apache.plc4x.java.can.field.CANFieldHandler;
+import org.apache.plc4x.java.can.field.CANOpenFieldHandler;
 import org.apache.plc4x.java.can.protocol.CANOpenProtocolLogic;
-import org.apache.plc4x.java.can.protocol.CANProtocolLogic;
 import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.socketcan.readwrite.io.SocketCANFrameIO;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
 import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
-import tel.schich.javacan.CanFrame;
 
-import java.util.function.Consumer;
 import java.util.function.ToIntFunction;
 
 /**
@@ -56,21 +53,32 @@ public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
     }
 
     @Override
+    protected boolean canRead() {
+        return true;
+    }
+
+    @Override
+    protected boolean canWrite() {
+        return true;
+    }
+
+    @Override
     protected String getDefaultTransport() {
         return "javacan";
     }
 
     @Override
-    protected CANFieldHandler getFieldHandler() {
-        return new CANFieldHandler();
+    protected CANOpenFieldHandler getFieldHandler() {
+        return new CANOpenFieldHandler();
     }
 
     @Override
     protected ProtocolStackConfigurer<SocketCANFrame> getStackConfigurer() {
         return SingleProtocolStackConfigurer.builder(SocketCANFrame.class, SocketCANFrameIO.class)
             .withProtocol(CANOpenProtocolLogic.class)
-            .withDriverContext(CANDriverContext.class)
+            .withDriverContext(CANOpenDriverContext.class)
             .withPacketSizeEstimator(CANEstimator.class)
+            .littleEndian()
             .build();
     }
 
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANFrame.java
new file mode 100644
index 0000000..ff1cad6
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANFrame.java
@@ -0,0 +1,11 @@
+package org.apache.plc4x.java.can.api;
+
+public interface CANFrame {
+
+    int getIdentifier();
+    boolean getExtended();
+    boolean getRemote();
+    boolean getError();
+    byte[] getData();
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANOpenFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANOpenFrame.java
new file mode 100644
index 0000000..27db3c2
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANOpenFrame.java
@@ -0,0 +1,66 @@
+package org.apache.plc4x.java.can.api;
+
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+
+public class CANOpenFrame implements CANFrame {
+
+    private final CANFrame frame;
+
+    private final CANOpenPayload payload;
+
+    public CANOpenFrame(CANFrame frame) {
+        this.frame = frame;
+        try {
+            this.payload = CANOpenPayloadIO.staticParse(new ReadBuffer(frame.getData(), true), serviceId(frame.getIdentifier()));
+        } catch (ParseException e) {
+            throw new PlcRuntimeException("Could not parse CANopen payload", e);
+        }
+    }
+
+    public CANOpenPayload getPayload() {
+        return payload;
+    }
+
+    @Override
+    public int getIdentifier() {
+        return frame.getIdentifier();
+    }
+
+    @Override
+    public boolean getExtended() {
+        return frame.getExtended();
+    }
+
+    @Override
+    public boolean getRemote() {
+        return frame.getRemote();
+    }
+
+    @Override
+    public boolean getError() {
+        return frame.getError();
+    }
+
+    @Override
+    public byte[] getData() {
+        return frame.getData();
+    }
+
+    private CANOpenService serviceId(int cobId) {
+        // form 32 bit socketcan identifier
+        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
+        if (service == null) {
+            for (CANOpenService val : CANOpenService.values()) {
+                if (val.getMin() > cobId && val.getMax() < cobId) {
+                    return val;
+                }
+            }
+        }
+        return service;
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
new file mode 100644
index 0000000..6df8b0c
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
@@ -0,0 +1,18 @@
+package org.apache.plc4x.java.can.api.conversation.canopen;
+
+import org.apache.plc4x.java.can.api.CANFrame;
+
+import java.util.function.BiConsumer;
+
+import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
+
+public interface CANConversation<W extends CANFrame> {
+
+    CANFrameBuilder<W> frameBuilder();
+
+    void send(W frame, BiConsumer<RequestTransaction, SendRequestContext<W>> callback);
+
+
+}
+
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANFrameBuilder.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANFrameBuilder.java
new file mode 100644
index 0000000..53a68c3
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANFrameBuilder.java
@@ -0,0 +1,11 @@
+package org.apache.plc4x.java.can.api.conversation.canopen;
+
+public interface CANFrameBuilder<W> {
+
+    CANFrameBuilder<W> node(int node);
+
+    CANFrameBuilder<W> data(byte[] data);
+
+    W build();
+
+}
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
new file mode 100644
index 0000000..abe6aa0
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
@@ -0,0 +1,75 @@
+package org.apache.plc4x.java.can.api.conversation.canopen;
+
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
+
+import java.util.function.BiConsumer;
+
+public class CANOpenConversation<W extends CANFrame> {
+
+    private final int node;
+    private final CANConversation<W> delegate;
+
+    public CANOpenConversation(int node, CANConversation<W> delegate) {
+        this.node = node;
+        this.delegate = delegate;
+    }
+
+    public SDOConversation<W> sdo() {
+        return new SDOConversation<>(this);
+    }
+
+    public void send(CANOpenService service, CANOpenPayload payload, BiConsumer<RequestTransaction, SendRequestContext<CANOpenPayload>> callback) {
+        CANFrameBuilder<W> builder = delegate.frameBuilder();
+        W frame = builder.node(service.getMin() + node).data(serialize(payload)).build();
+        delegate.send(frame, (tx, ctx) -> {
+            SendRequestContext<CANOpenPayload> unwrap = ctx
+//                .onError((response, error) -> {
+//                    System.err.println("Unexpected frame " + response + " " + error);
+//                })
+            .unwrap(CANOpenConversation.this::deserialize);
+            callback.accept(tx, unwrap);
+        });
+    }
+
+    private CANOpenPayload deserialize(CANFrame frame) {
+        try {
+            CANOpenService service = serviceId(frame.getIdentifier());
+            ReadBuffer buffer = new ReadBuffer(frame.getData(), true);
+            return CANOpenPayloadIO.staticParse(buffer, service);
+        } catch (ParseException e) {
+            throw new PlcRuntimeException("Could not deserialize CAN open payload", e);
+        }
+    }
+
+    private byte[] serialize(CANOpenPayload payload) {
+        try {
+            WriteBuffer buffer = new WriteBuffer(payload.getLengthInBytes(), true);
+            CANOpenPayloadIO.staticSerialize(buffer, payload);
+            return buffer.getData();
+        } catch (ParseException e) {
+            throw new PlcRuntimeException("Could not serialize CAN open payload", e);
+        }
+    }
+
+    private CANOpenService serviceId(int cobId) {
+        // form 32 bit socketcan identifier
+        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
+        if (service == null) {
+            for (CANOpenService val : CANOpenService.values()) {
+                if (val.getMin() > cobId && val.getMax() < cobId) {
+                    return val;
+                }
+            }
+        }
+        return service;
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversationBase.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversationBase.java
new file mode 100644
index 0000000..60b87e4
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversationBase.java
@@ -0,0 +1,15 @@
+package org.apache.plc4x.java.can.api.conversation.canopen;
+
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+
+public abstract class CANOpenConversationBase {
+
+    protected PlcValue decodeFrom(byte[] data, CANOpenDataType type, int length) throws ParseException {
+        return DataItemIO.staticParse(new ReadBuffer(data, true), type, length);
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOConversation.java
new file mode 100644
index 0000000..1447f08
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOConversation.java
@@ -0,0 +1,43 @@
+package org.apache.plc4x.java.can.api.conversation.canopen;
+
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse;
+import org.apache.plc4x.java.canopen.readwrite.IndexAddress;
+import org.apache.plc4x.java.canopen.readwrite.SDORequest;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
+
+import java.util.function.BiConsumer;
+
+public class SDOConversation<W extends CANFrame> {
+
+    private final CANOpenConversation<W> delegate;
+
+    public SDOConversation(CANOpenConversation<W> delegate) {
+        this.delegate = delegate;
+    }
+
+    public SDODownloadConversation<W> download(IndexAddress indexAddress, PlcValue value, CANOpenDataType type) {
+        return new SDODownloadConversation<>(this, indexAddress, value, type);
+    }
+
+    public SDOUploadConversation<W> upload(IndexAddress indexAddress, CANOpenDataType type) {
+        return new SDOUploadConversation<>(this, indexAddress, type);
+    }
+
+    public void send(SDORequest request, BiConsumer<RequestTransaction, SendRequestContext<CANOpenSDOResponse>> callback) {
+        delegate.send(CANOpenService.RECEIVE_SDO, new CANOpenSDORequest(request.getCommand(), request), (tx, ctx) -> {
+            SendRequestContext<CANOpenSDOResponse> context = ctx
+//            .onError((response, error) -> {
+//                System.out.println("Unexpected frame " + response + " " + error);
+//            })
+            .only(CANOpenSDOResponse.class);
+            callback.accept(tx, context);
+        });
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDODownloadConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDODownloadConversation.java
new file mode 100644
index 0000000..470e190
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDODownloadConversation.java
@@ -0,0 +1,97 @@
+package org.apache.plc4x.java.can.api.conversation.canopen;
+
+import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.api.segmentation.accumulator.ByteStorage;
+import org.apache.plc4x.java.canopen.readwrite.*;
+import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.apache.plc4x.java.canopen.readwrite.types.SDOResponseCommand;
+import org.apache.plc4x.java.spi.generation.ParseException;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiConsumer;
+
+public class SDODownloadConversation<W extends CANFrame> {
+
+    private final SDOConversation<W> delegate;
+    private final IndexAddress indexAddress;
+    private final byte[] data;
+
+    public SDODownloadConversation(SDOConversation<W> delegate, IndexAddress indexAddress, PlcValue value, CANOpenDataType type) {
+        this.delegate = delegate;
+        this.indexAddress = indexAddress;
+
+        try {
+            data = DataItemIO.staticSerialize(value, type,  null,true).getData();
+        } catch (ParseException e) {
+            throw new PlcRuntimeException("Could not serialize data", e);
+        }
+    }
+
+    public void execute(BiConsumer<PlcResponseCode, Throwable> receiver) throws PlcException {
+        if (data.length > 4) {
+            // segmented
+
+            SDOInitiateSegmentedUploadResponse size = new SDOInitiateSegmentedUploadResponse(data.length);
+            delegate.send(new SDOInitiateDownloadRequest(false, true, indexAddress, size), (tx, ctx) -> {
+                ctx.unwrap(CANOpenSDOResponse::getResponse)
+                    .check(p -> p.getCommand() == SDOResponseCommand.INITIATE_DOWNLOAD)
+                    .only(SDOInitiateDownloadResponse.class)
+                    .check(p -> indexAddress.equals(p.getAddress()))
+                    .handle(x -> {
+                        put(data, receiver, false, 0);
+                    });
+            });
+
+            return;
+        }
+
+        // expedited
+        SDOInitiateDownloadRequest rq = new SDOInitiateDownloadRequest(
+            true, true,
+            indexAddress,
+            new SDOInitiateExpeditedUploadResponse(data)
+        );
+
+        delegate.send(rq, (tx, ctx) ->
+            ctx.onError((response, error) -> {
+                System.out.println("Unexpected frame " + response + " " + error);
+            })
+            .unwrap(CANOpenSDOResponse::getResponse)
+            .check(r -> r.getCommand() == SDOResponseCommand.INITIATE_DOWNLOAD)
+            .handle(r -> {
+                System.out.println(r);
+            })
+        );
+    }
+
+    private void put(byte[] data, BiConsumer<PlcResponseCode, Throwable> receiver, boolean toggle, int offset) {
+        int remaining = data.length - offset;
+        byte[] segment = new byte[Math.min(remaining, 7)];
+        System.arraycopy(data, offset, segment, 0, segment.length);
+
+        delegate.send(new SDOSegmentDownloadRequest(toggle, remaining <= 7, segment), (tx, ctx) -> {
+            ctx.unwrap(CANOpenSDOResponse::getResponse)
+                .only(SDOSegmentDownloadResponse.class)
+                .onError((response, error) -> {
+                    System.out.println("Unexpected frame " + response + " " + error);
+                    receiver.accept(null, error);
+                })
+                .check(r -> r.getToggle() == toggle)
+                .handle(reply -> {
+                    if (offset + segment.length == data.length) {
+                        // validate offset
+                        receiver.accept(PlcResponseCode.OK, null);
+                    } else {
+                        put(data, receiver, !toggle, offset + segment.length);
+                    }
+                });
+        });
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
new file mode 100644
index 0000000..cb2d778
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
@@ -0,0 +1,98 @@
+package org.apache.plc4x.java.can.api.conversation.canopen;
+
+import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.api.segmentation.accumulator.ByteStorage;
+import org.apache.plc4x.java.canopen.readwrite.*;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.apache.plc4x.java.spi.generation.ParseException;
+
+import java.util.function.BiConsumer;
+
+public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversationBase {
+
+    private final SDOConversation<W> delegate;
+    private final IndexAddress address;
+    private final CANOpenDataType type;
+
+    public SDOUploadConversation(SDOConversation<W> delegate, IndexAddress address, CANOpenDataType type) {
+        this.delegate = delegate;
+        this.address = address;
+        this.type = type;
+    }
+
+    public void execute(BiConsumer<PlcValue, Throwable> receiver) throws PlcException {
+        SDOInitiateUploadRequest rq = new SDOInitiateUploadRequest(address);
+
+        delegate.send(rq, (tx, ctx) ->
+            ctx
+//            .onError((response, error) -> {
+//                System.err.println("Unexpected frame " + response + " " + error);
+//                receiver.accept(null, error);
+//            })
+            .unwrap(CANOpenSDOResponse::getResponse)
+            .onError(((response, error) -> {
+                if (response instanceof SDOAbortResponse) {
+                    SDOAbortResponse abort = (SDOAbortResponse) response;
+                    SDOAbort sdoAbort = abort.getAbort();
+                    receiver.accept(null, new PlcException("Could not read value. Remote party reported code " + sdoAbort.getCode()));
+                } else {
+                    receiver.accept(null, error);
+                }
+            }))
+            .only(SDOInitiateUploadResponse.class)
+            .check(r -> r.getAddress().equals(address))
+            .handle(response -> {
+                handle(receiver, response);
+            })
+        );
+    }
+
+    private void handle(BiConsumer<PlcValue, Throwable> receiver, SDOInitiateUploadResponse answer) {
+        BiConsumer<Integer, byte[]> valueCallback = (length, bytes) -> {
+            try {
+                receiver.accept(decodeFrom(bytes, type, length), null);
+            } catch (ParseException e) {
+                receiver.accept(null, e);
+            }
+        };
+
+        if (answer.getExpedited() && answer.getIndicated() && answer.getPayload() instanceof SDOInitiateExpeditedUploadResponse) {
+            SDOInitiateExpeditedUploadResponse payload = (SDOInitiateExpeditedUploadResponse) answer.getPayload();
+            valueCallback.accept(payload.getData().length, payload.getData());
+        } else if (answer.getPayload() instanceof SDOInitiateSegmentedUploadResponse) {
+            ByteStorage.SDOUploadStorage storage = new ByteStorage.SDOUploadStorage();
+            storage.append(answer);
+
+            SDOInitiateSegmentedUploadResponse segment = (SDOInitiateSegmentedUploadResponse) answer.getPayload();
+            fetch(storage, valueCallback, receiver, false, Long.valueOf(segment.getBytes()).intValue());
+        } else {
+            receiver.accept(null, new PlcException("Unsupported SDO operation kind."));
+        }
+    }
+
+    private void fetch(ByteStorage.SDOUploadStorage storage, BiConsumer<Integer, byte[]> valueCallback, BiConsumer<PlcValue, Throwable> receiver, boolean toggle, int size) {
+        delegate.send(new SDOSegmentUploadRequest(toggle), (tx, ctx) -> {
+            ctx.unwrap(CANOpenSDOResponse::getResponse)
+                .only(SDOSegmentUploadResponse.class)
+                .onError((response, error) -> {
+                    System.out.println("Unexpected frame " + response + " " + error);
+                    receiver.accept(null, error);
+                })
+                .check(r -> r.getToggle() == toggle)
+                .handle(reply -> {
+                    storage.append(reply);
+
+                    if (reply.getLast()) {
+                        // validate size
+                        valueCallback.accept(Long.valueOf(size).intValue(), storage.get());
+                    } else {
+                        fetch(storage, valueCallback, receiver, !toggle, size);
+                    }
+                });
+        });
+    }
+
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/PlcSegmentationException.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/PlcSegmentationException.java
new file mode 100644
index 0000000..1023551
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/PlcSegmentationException.java
@@ -0,0 +1,21 @@
+package org.apache.plc4x.java.can.api.segmentation;
+
+import org.apache.plc4x.java.api.exceptions.PlcException;
+
+public class PlcSegmentationException extends PlcException {
+    public PlcSegmentationException(String message) {
+        super(message);
+    }
+
+    public PlcSegmentationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public PlcSegmentationException(Throwable cause) {
+        super(cause);
+    }
+
+    public PlcSegmentationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/Segmentation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/Segmentation.java
new file mode 100644
index 0000000..84f915d
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/Segmentation.java
@@ -0,0 +1,131 @@
+/*
+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.can.api.segmentation;
+
+import org.apache.plc4x.java.can.api.segmentation.accumulator.Storage;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * Utility type for handling segmented request-response operations.
+ *
+ * Segmented operation is one which spans over multiple frames and contains of:
+ * - initialization
+ * - 0... n steps
+ * - finalization
+ *
+ * Depending on use case there might be 0 intermediate frames, leading to situation where requester sends segment request
+ * and answer comes in one shot.
+ */
+public abstract class Segmentation<C, T, R> {
+
+    private final Duration timeout;
+    private final Storage<T, R> storage;
+    private final Class<C> frameType;
+
+    private Supplier<T> request;
+    private Predicate<T> responseValidator;
+
+    private Predicate<T> finalStep;
+    private Consumer<List<T>> callback;
+    private Function<T, T> step;
+    private Predicate<T> stepValidator;
+    private List<T> answers = new ArrayList<>();
+
+    public Segmentation(Class<C> frameType, Duration timeout, Storage<T, R> storage) {
+        this.frameType = frameType;
+        this.timeout = timeout;
+        this.storage = storage;
+    }
+
+    public Segmentation<C, T, R> begin(Supplier<T> request, Predicate<T> requestAnswer) {
+        this.request = request;
+        this.responseValidator = requestAnswer;
+        return this;
+    }
+
+    public Segmentation<C, T, R> step(Function<T, T> step, Predicate<T> stepAnswer) {
+        this.step = step;
+        this.stepValidator = stepAnswer;
+        return this;
+    }
+
+    public Segmentation<C, T, R> end(Predicate<T> finalStep, Consumer<List<T>> callback) {
+        this.finalStep = finalStep;
+        this.callback = callback;
+        return this;
+    }
+
+    public void execute(RequestTransactionManager tm, ConversationContext<C> context) throws PlcSegmentationException {
+        Consumer<T> consumer = new Consumer<T>() {
+            @Override
+            public void accept(T payload) {
+                storage.append(payload);
+
+                if (finalStep.test(payload)) {
+                    callback.accept(answers);
+                } else {
+                    try {
+                        T apply = step.apply(payload);
+                        send(tm, context, () -> apply, stepValidator, this);
+                    } catch (PlcSegmentationException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        };
+        try {
+            send(tm, context, request, responseValidator, consumer);
+        } catch (RuntimeException e) {
+            if (e.getCause() != null) {
+                throw new PlcSegmentationException(e.getCause());
+            }
+            throw new PlcSegmentationException(e);
+        }
+    }
+
+    private void send(RequestTransactionManager tm, ConversationContext<C> context, Supplier<T> generator, Predicate<T> predicate, Consumer<T> callback) throws PlcSegmentationException {
+        C request = wrap(generator.get());
+
+        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        transaction.submit(() -> context.sendRequest(request)
+            .expectResponse(frameType, timeout)
+            .unwrap(this::unwrap)
+            .check(predicate)
+            .onError((payload, error) -> transaction.endRequest())
+            .handle(payload -> {
+                callback.accept(payload);
+                transaction.endRequest();
+            })
+        );
+    }
+
+    protected abstract T unwrap(C frame);
+
+    protected abstract C wrap(T payload) throws PlcSegmentationException;
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/ByteStorage.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/ByteStorage.java
new file mode 100644
index 0000000..1f143f2
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/ByteStorage.java
@@ -0,0 +1,59 @@
+package org.apache.plc4x.java.can.api.segmentation.accumulator;
+
+import org.apache.plc4x.java.canopen.readwrite.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+public class ByteStorage<T> implements Storage<T, byte[]> {
+
+    private final List<byte[]> segments = new ArrayList<>();
+    private final Function<T, byte[]> extractor;
+    private long size = 0;
+
+    public ByteStorage(Function<T, byte[]> extractor) {
+        this.extractor = extractor;
+    }
+
+    @Override
+    public void append(T frame) {
+        segments.add(extractor.apply(frame));
+        size += segments.get(segments.size() - 1).length;
+    }
+
+    public long size() {
+        return size;
+    }
+
+    @Override
+    public byte[] get() {
+        Optional<byte[]> collect = segments.stream().reduce((b1, b2) -> {
+            byte[] combined = new byte[b1.length + b2.length];
+            System.arraycopy(b1, 0, combined, 0, b1.length);
+            System.arraycopy(b2, 0, combined, b1.length, b2.length);
+            return combined;
+        });
+        return collect.orElse(new byte[0]);
+    }
+
+    public static class SDOUploadStorage extends ByteStorage<SDOResponse> {
+        public SDOUploadStorage() {
+            super((sdoResponse -> {
+                if (sdoResponse instanceof SDOSegmentUploadResponse) {
+                    return ((SDOSegmentUploadResponse) sdoResponse).getData();
+                }
+                if (sdoResponse instanceof SDOInitiateUploadResponse) {
+                    SDOInitiateUploadResponse initiate = (SDOInitiateUploadResponse) sdoResponse;
+
+                    if (initiate.getPayload() instanceof SDOInitiateExpeditedUploadResponse) {
+                        return ((SDOInitiateExpeditedUploadResponse) initiate.getPayload()).getData();
+                    }
+                }
+                return new byte[0];
+            }));
+        }
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/Storage.java
similarity index 57%
copy from sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
copy to sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/Storage.java
index 8212bea..45c9567 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/Storage.java
@@ -16,21 +16,35 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 */
-package org.apache.plc4x.java.can;
-
-import org.apache.plc4x.java.PlcDriverManager;
-import org.apache.plc4x.java.api.PlcConnection;
+package org.apache.plc4x.java.can.api.segmentation.accumulator;
 
 /**
- * Here we begin .. ;-)
+ * A storage which is called for each received segment.
+ *
+ * @param <T> Type of frame.
+ * @param <R> Type of result.
  */
-public class Main {
-
-    public static void main(String[] args) throws Exception {
-        PlcDriverManager driverManager = new PlcDriverManager();
-
-        PlcConnection connection = driverManager.getConnection("canopen:javacan://vcan0?nodeId=11");
-
-    }
+public interface Storage<T, R> {
+
+    /**
+     * Appends segmented frame.
+     *
+     * @param frame Segmented frame.
+     */
+    void append(T frame);
+
+    /**
+     * Gets accumulated size of stored data.
+     *
+     * @return Occupied memory in bytes.
+     */
+    long size();
+
+    /**
+     * Retrieves final result from segmented payload.
+     *
+     * @return Assembled result.
+     */
+    R get();
 
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
index 214794d..fcb7194 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
@@ -42,8 +42,8 @@ public class CANConfiguration implements Configuration, CANTransportConfiguratio
         return hearbeat;
     }
 
-    public void setHearbeat(boolean hearbeat) {
-        this.hearbeat = hearbeat;
+    public void setHeartbeat(boolean heartbeat) {
+        this.hearbeat = heartbeat;
     }
 
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/context/CANOpenDriverContext.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/context/CANOpenDriverContext.java
new file mode 100644
index 0000000..31c0028
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/context/CANOpenDriverContext.java
@@ -0,0 +1,9 @@
+package org.apache.plc4x.java.can.context;
+
+import org.apache.plc4x.java.can.listener.CompositeCallback;
+
+public class CANOpenDriverContext extends CANDriverContext {
+
+    public final static CompositeCallback CALLBACK = new CompositeCallback();
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
new file mode 100644
index 0000000..6458848
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
@@ -0,0 +1,51 @@
+/*
+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.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+
+import java.util.regex.Pattern;
+
+/**
+ * Generic field type which defines node address and address pattern (index/subindex).
+ */
+public abstract class CANOpenField implements PlcField {
+
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?:(0[xX](?<indexHex>[0-9a-fA-F]+))|(?<index>\\d+))/(?:(0[xX](?<subIndexHex>[0-9a-fA-F]+))|(?<subIndex>\\d+)):(?<canDataType>\\w+)(\\[(?<numberOfElements>\\d)])?");
+    public static final Pattern NODE_PATTERN = Pattern.compile("(?<nodeId>\\d+)");
+
+    private final int nodeId;
+
+    public CANOpenField(int nodeId) {
+        this.nodeId = nodeId;
+    }
+
+    public int getNodeId() {
+        return nodeId;
+    }
+
+    public static CANOpenField of(String addressString) throws PlcInvalidFieldException {
+        if (CANOpenSDOField.matches(addressString)) {
+            return CANOpenSDOField.of(addressString);
+        }
+
+        throw new PlcInvalidFieldException("Unable to parse address: " + addressString);
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenFieldHandler.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenFieldHandler.java
new file mode 100644
index 0000000..cf37533
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenFieldHandler.java
@@ -0,0 +1,58 @@
+/*
+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.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.value.PlcList;
+import org.apache.plc4x.java.api.value.PlcString;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.spi.connection.DefaultPlcFieldHandler;
+import org.apache.plc4x.java.spi.connection.PlcFieldHandler;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+public class CANOpenFieldHandler extends DefaultPlcFieldHandler implements PlcFieldHandler {
+
+    @Override
+    public PlcField createField(String fieldQuery) throws PlcInvalidFieldException {
+        return CANOpenField.of(fieldQuery);
+    }
+
+    @Override
+    public PlcValue encodeString(PlcField field, Object[] values) {
+        CANOpenSDOField coField = (CANOpenSDOField) field;
+        String[] strings = (String[]) values;
+
+        switch (coField.getCanOpenDataType()) {
+            case VISIBLE_STRING:
+            case OCTET_STRING:
+            case UNICODE_STRING:
+                if (values.length == 1) {
+                    return new PlcString(strings[0]);
+                } else {
+                    return new PlcList(Arrays.stream(strings).map(PlcString::new).collect(Collectors.toList()));
+                }
+        }
+
+        throw new PlcRuntimeException("Invalid encoder for type " + coField.getCanOpenDataType().name());
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java
new file mode 100644
index 0000000..83bba15
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java
@@ -0,0 +1,58 @@
+/*
+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.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CANOpenNMTField extends CANOpenField {
+
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("NMT|NMT:" + CANOpenField.NODE_PATTERN);
+
+    public CANOpenNMTField(int node) {
+        super(node);
+    }
+
+    public boolean isWildcard() {
+        return getNodeId() == 0;
+    }
+
+    public static boolean matches(String addressString) {
+        return ADDRESS_PATTERN.matcher(addressString).matches();
+    }
+
+    public static Matcher getMatcher(String addressString) throws PlcInvalidFieldException {
+        Matcher matcher = ADDRESS_PATTERN.matcher(addressString);
+        if (matcher.matches()) {
+            return matcher;
+        }
+
+        throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN);
+    }
+
+    public static CANOpenNMTField of(String addressString) {
+        Matcher matcher = getMatcher(addressString);
+        int nodeId = Integer.parseInt(matcher.group("nodeId"));
+
+        return new CANOpenNMTField(nodeId);
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenSDOField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenSDOField.java
new file mode 100644
index 0000000..86882c0
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenSDOField.java
@@ -0,0 +1,89 @@
+/*
+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.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CANOpenSDOField extends CANOpenField {
+
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("SDO:" + CANOpenField.NODE_PATTERN + ":" + CANOpenField.ADDRESS_PATTERN);
+    private final short index;
+    private final short subIndex;
+    private final CANOpenDataType canOpenDataType;
+
+    public CANOpenSDOField(int node, short index, short subIndex, CANOpenDataType canOpenDataType) {
+        super(node);
+        this.index = index;
+        this.subIndex = subIndex;
+        this.canOpenDataType = canOpenDataType;
+    }
+
+    public short getIndex() {
+        return index;
+    }
+
+    public short getSubIndex() {
+        return subIndex;
+    }
+
+    public CANOpenDataType getCanOpenDataType() {
+        return canOpenDataType;
+    }
+
+    public static boolean matches(String addressString) {
+        return ADDRESS_PATTERN.matcher(addressString).matches();
+    }
+
+    public static Matcher getMatcher(String addressString) throws PlcInvalidFieldException {
+        Matcher matcher = ADDRESS_PATTERN.matcher(addressString);
+        if (matcher.matches()) {
+            return matcher;
+        }
+
+        throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN);
+    }
+
+    public static CANOpenSDOField of(String addressString) {
+        Matcher matcher = getMatcher(addressString);
+        int nodeId = Integer.parseInt(matcher.group("nodeId"));
+
+        short index = parseHex(matcher.group("indexHex"), matcher.group("index"));
+        short subIndex = parseHex(matcher.group("subIndexHex"), matcher.group("subIndex"));
+
+        String canDataTypeString = matcher.group("canDataType");
+        CANOpenDataType canOpenDataType = CANOpenDataType.valueOf(canDataTypeString);
+
+        //String numberOfElementsString = matcher.group("numberOfElements");
+        //Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
+
+        return new CANOpenSDOField(nodeId, index, subIndex, canOpenDataType);
+    }
+
+    private static Short parseHex(String hex, String dec) {
+        if (hex != null) {
+            return Short.parseShort(hex, 16);
+        }
+        return Short.parseShort(dec);
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
index 1a9a346..3f1e22a 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
@@ -1,9 +1,12 @@
 package org.apache.plc4x.java.can.helper;
 
+import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.canopen.readwrite.SDOInitiateExpeditedUploadResponse;
 import org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadResponsePayload;
 import org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadResponse;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
 import static org.apache.plc4x.java.spi.generation.StaticHelper.COUNT;
@@ -26,4 +29,15 @@ public class CANOpenHelper {
         // NOOP - a placeholder to let mspec compile
     }
 
+    public static Object parseString(ReadBuffer io, int length, String charset) {
+        return io.readString(8 * length, charset);
+    }
+
+    public static void serializeString(WriteBuffer io, PlcValue value, String charset) throws ParseException {
+        io.writeString(8, charset, value.getString());
+    }
+
+    public static byte[] parseByteArray(ReadBuffer io, Integer length) {
+        return new byte[0];
+    }
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
index 2ad7a1c..9e549de 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/HeaderParser.java
@@ -6,6 +6,13 @@ import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
 public class HeaderParser {
 
+    public static final int EFF_FLAG = 0b10000000_00000000_00000000_00000000;
+    public static final int RTR_FLAG = 0b01000000_00000000_00000000_00000000;
+    public static final int ERR_FLAG = 0b00100000_00000000_00000000_00000000;
+    public static final int SFF_MASK = 0b00000000_00000000_00000111_11111111;
+    public static final int EFF_MASK = 0b00011111_11111111_11111111_11111111;
+    public static final int ERR_MASK = EFF_MASK;
+
     public static final int EXTENDED_FRAME_FORMAT_FLAG = 0x80000000;
 
     public static final int REMOTE_TRANSMISSION_FLAG = 0x40000000;
@@ -17,10 +24,10 @@ public class HeaderParser {
     public static final int EXTENDED_FORMAT_IDENTIFIER_MASK = 0x1fffffff;
 
     public static int readIdentifier(int identifier) {
-        if ((identifier & EXTENDED_FORMAT_IDENTIFIER_MASK) == 0) {
-            return identifier & STANDARD_FORMAT_IDENTIFIER_MASK;
+        if ((isExtended(identifier))) {
+            return identifier & EXTENDED_FORMAT_IDENTIFIER_MASK;
         }
-        return identifier & EXTENDED_FORMAT_IDENTIFIER_MASK;
+        return identifier & STANDARD_FORMAT_IDENTIFIER_MASK;
     }
 
     public static void writeIdentifier(WriteBuffer buffer, SocketCANFrame frame) throws ParseException {
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/listener/Callback.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/listener/Callback.java
new file mode 100644
index 0000000..74d8764
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/listener/Callback.java
@@ -0,0 +1,8 @@
+package org.apache.plc4x.java.can.listener;
+
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+
+public interface Callback {
+    void receive(SocketCANFrame frame);
+}
+
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/listener/CompositeCallback.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/listener/CompositeCallback.java
new file mode 100644
index 0000000..3ff5454
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/listener/CompositeCallback.java
@@ -0,0 +1,25 @@
+package org.apache.plc4x.java.can.listener;
+
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class CompositeCallback implements Callback {
+
+    private List<Callback> callbacks = new CopyOnWriteArrayList<>();
+
+    @Override
+    public void receive(SocketCANFrame frame) {
+        callbacks.forEach(callback -> callback.receive(frame));
+    }
+
+    public boolean addCallback(Callback callback) {
+        return callbacks.add(callback);
+    }
+
+    public boolean removeCallback(Callback callback) {
+        return callbacks.remove(callback);
+    }
+}
+
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index fa6dab2..b5aa34e 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -18,34 +18,58 @@ under the License.
 */
 package org.apache.plc4x.java.can.protocol;
 
+import org.apache.plc4x.java.api.messages.*;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.PlcNull;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.api.conversation.canopen.CANConversation;
+import org.apache.plc4x.java.can.api.conversation.canopen.CANOpenConversation;
+import org.apache.plc4x.java.can.api.conversation.canopen.SDODownloadConversation;
+import org.apache.plc4x.java.can.api.conversation.canopen.SDOUploadConversation;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
-import org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload;
-import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.can.context.CANOpenDriverContext;
+import org.apache.plc4x.java.can.field.CANOpenField;
+import org.apache.plc4x.java.can.field.CANOpenSDOField;
+import org.apache.plc4x.java.can.socketcan.SocketCANConversation;
+import org.apache.plc4x.java.canopen.readwrite.*;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenHeartbeatPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
+import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.canopen.readwrite.types.NMTState;
 import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.configuration.HasConfiguration;
+import org.apache.plc4x.java.spi.context.DriverContext;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.messages.*;
+import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.CompletableFuture;
 
 public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> implements HasConfiguration<CANConfiguration> {
 
+    private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10L);
     private Logger logger = LoggerFactory.getLogger(CANOpenProtocolLogic.class);
 
     private CANConfiguration configuration;
     private RequestTransactionManager tm;
     private Timer heartbeat;
+    private CANOpenDriverContext canContext;
+    private CANConversation<CANFrame> conversation;
 
     @Override
     public void setConfiguration(CANConfiguration configuration) {
@@ -55,11 +79,23 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     @Override
+    public void setDriverContext(DriverContext driverContext) {
+        super.setDriverContext(driverContext);
+        this.canContext = (CANOpenDriverContext) driverContext;
+
+        // Initialize Transaction Manager.
+        // Until the number of concurrent requests is successfully negotiated we set it to a
+        // maximum of only one request being able to be sent at a time. During the login process
+        // No concurrent requests can be sent anyway. It will be updated when receiving the
+        // S7ParameterSetupCommunication response.
+        this.tm = new RequestTransactionManager(1);
+    }
+
+    @Override
     public void onConnect(ConversationContext<SocketCANFrame> context) {
         try {
             if (configuration.isHeartbeat()) {
                 context.sendToWire(createFrame(new CANOpenHeartbeatPayload(NMTState.BOOTED_UP)));
-                context.fireConnected();
 
                 this.heartbeat = new Timer();
                 this.heartbeat.scheduleAtFixedRate(new TimerTask() {
@@ -73,15 +109,106 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
                     }
                 }, 5000, 5000);
             }
+            context.fireConnected();
         } catch (ParseException e) {
             e.printStackTrace();
         }
     }
 
+    @Override
+    public void setContext(ConversationContext<SocketCANFrame> context) {
+        super.setContext(context);
+        this.conversation = new SocketCANConversation(tm, context);
+    }
+
     private SocketCANFrame createFrame(CANOpenHeartbeatPayload state) throws ParseException {
-        WriteBuffer buffer = new WriteBuffer(state.getLengthInBytes());
+        WriteBuffer buffer = new WriteBuffer(state.getLengthInBytes(), true);
         CANOpenHeartbeatPayloadIO.staticSerialize(buffer, state);
-        return new SocketCANFrame(cobId(CANOpenService.HEARTBEAT), buffer.getData());
+        return new SocketCANFrame(cobId(configuration.getNodeId(), CANOpenService.HEARTBEAT), buffer.getData());
+    }
+
+    public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
+        CompletableFuture<PlcWriteResponse> response = new CompletableFuture<>();
+        if (writeRequest.getFieldNames().size() != 1) {
+            response.completeExceptionally(new IllegalArgumentException("Unsupported field"));
+            return response;
+        }
+
+        PlcField field = writeRequest.getFields().get(0);
+        if (!(field instanceof CANOpenField)) {
+            response.completeExceptionally(new IllegalArgumentException("Only CANOpenField instances are supported"));
+            return response;
+        }
+
+        if (!(field instanceof CANOpenSDOField)) {
+            response.completeExceptionally(new IllegalArgumentException("Only CANOpenSDOField instances are supported"));
+            return response;
+        };
+
+        writeInternally((InternalPlcWriteRequest) writeRequest, (CANOpenSDOField) field, response);
+        return response;
+    }
+
+    private void writeInternally(InternalPlcWriteRequest writeRequest, CANOpenSDOField field, CompletableFuture<PlcWriteResponse> response) {
+        CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(field.getNodeId(), conversation);
+
+        PlcValue writeValue = writeRequest.getPlcValues().get(0);
+
+        SDODownloadConversation<CANFrame> download = canopen.sdo().download(new IndexAddress(field.getIndex(), field.getSubIndex()), writeValue, field.getCanOpenDataType());
+        try {
+            download.execute((value, error) -> {
+                String fieldName = writeRequest.getFieldNames().iterator().next();
+                Map<String, PlcResponseCode> fields = new HashMap<>();
+                fields.put(fieldName, PlcResponseCode.OK);
+                response.complete(new DefaultPlcWriteResponse(writeRequest, fields));
+            });
+        } catch (Exception e) {
+            response.completeExceptionally(e);
+        }
+    }
+
+    public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
+        CompletableFuture<PlcReadResponse> response = new CompletableFuture<>();
+        if (readRequest.getFieldNames().size() != 1) {
+            response.completeExceptionally(new IllegalArgumentException("SDO requires single field to be read"));
+            return response;
+        }
+
+        PlcField field = readRequest.getFields().get(0);
+        if (!(field instanceof CANOpenField)) {
+            response.completeExceptionally(new IllegalArgumentException("Only CANOpenField instances are supported"));
+            return response;
+        }
+
+        if (!(field instanceof CANOpenSDOField)) {
+            response.completeExceptionally(new IllegalArgumentException("Only CANOpenSDOField instances are supported"));
+            return response;
+        };
+
+        readInternally((InternalPlcReadRequest) readRequest, (CANOpenSDOField) field, response);
+        return response;
+    }
+
+    @Override
+    public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
+        ((InternalPlcSubscriptionRequest) subscriptionRequest).getSubscriptionFields().get(0).getPlcSubscriptionType();
+        return super.subscribe(subscriptionRequest);
+    }
+
+    private void readInternally(InternalPlcReadRequest readRequest, CANOpenSDOField field, CompletableFuture<PlcReadResponse> response) {
+        CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(field.getNodeId(), conversation);
+
+        SDOUploadConversation<CANFrame> upload = canopen.sdo().upload(new IndexAddress(field.getIndex(), field.getSubIndex()), field.getCanOpenDataType());
+        try {
+            upload.execute((value, error) -> {
+                String fieldName = readRequest.getFieldNames().iterator().next();
+                Map<String, ResponseItem<PlcValue>> fields = new HashMap<>();
+                fields.put(fieldName, new ResponseItem<>(PlcResponseCode.OK, value));
+                response.complete(new DefaultPlcReadResponse(readRequest, fields));
+            });
+        } catch (Exception e) {
+            response.completeExceptionally(e);
+        }
     }
 
     @Override
@@ -89,6 +216,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
         CANOpenService service = serviceId(msg.getIdentifier());
         CANOpenPayload payload = CANOpenPayloadIO.staticParse(new ReadBuffer(msg.getData()), service);
 
+        CANOpenDriverContext.CALLBACK.receive(msg);
+
         if (service != null) {
             logger.info("Decoded CANOpen {} from {}, message {}", service, Math.abs(service.getMin() - msg.getIdentifier()), payload);
         } else {
@@ -118,18 +247,18 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
         }
     }
 
-    private int cobId(CANOpenService service) {
+    private int cobId(int nodeId, CANOpenService service) {
         // form 32 bit socketcan identifier
-        return (configuration.getNodeId() << 24) & 0xff000000 |
+        return (nodeId << 24) & 0xff000000 |
             (service.getValue() << 16 ) & 0x00ff0000;
     }
 
-    private CANOpenService serviceId(int nodeId) {
+    private CANOpenService serviceId(int cobId) {
         // form 32 bit socketcan identifier
-        CANOpenService service = CANOpenService.valueOf((byte) (nodeId >> 7));
+        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
         if (service == null) {
             for (CANOpenService val : CANOpenService.values()) {
-                if (val.getMin() > nodeId && val.getMax() < nodeId) {
+                if (val.getMin() > cobId && val.getMax() < cobId) {
                     return val;
                 }
             }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/segmentation/CANOpenSegmentation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/segmentation/CANOpenSegmentation.java
new file mode 100644
index 0000000..afea76e
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/segmentation/CANOpenSegmentation.java
@@ -0,0 +1,86 @@
+package org.apache.plc4x.java.can.protocol.segmentation;
+
+import org.apache.plc4x.java.can.api.segmentation.Segmentation;
+import org.apache.plc4x.java.can.api.segmentation.accumulator.Storage;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * A basic utility to execute segmented operations.
+ */
+public class CANOpenSegmentation<R> extends Segmentation<SocketCANFrame, CANOpenPayload, R> {
+
+    private final CANOpenService service;
+    private final int node;
+
+    public CANOpenSegmentation(CANOpenService service, int node, Storage<CANOpenPayload, R> storage) {
+        super(SocketCANFrame.class, Duration.ofSeconds(10L), storage);
+
+        this.service = service;
+        this.node = node;
+    }
+
+    public CANOpenSegmentation<R> begin(Supplier<CANOpenPayload> request, Predicate<CANOpenPayload> requestAnswer) {
+        super.begin(request, requestAnswer);
+        return this;
+    }
+
+    public CANOpenSegmentation<R> step(Function<CANOpenPayload, CANOpenPayload> step, Predicate<CANOpenPayload> stepAnswer) {
+        super.step(step, stepAnswer);
+        return this;
+    }
+
+    public CANOpenSegmentation<R> end(Predicate<CANOpenPayload> finalStep, Consumer<List<CANOpenPayload>> callback) {
+        super.end(finalStep, callback);
+        return this;
+    }
+
+    protected CANOpenPayload unwrap(SocketCANFrame frame) {
+        return unsecure(() -> CANOpenPayloadIO.staticParse(new ReadBuffer(frame.getData()), serviceId(frame.getIdentifier())));
+    }
+
+    private <T> T unsecure(Callable<T> statement) {
+        try {
+            return statement.call();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected SocketCANFrame wrap(CANOpenPayload payload) {
+        try {
+            WriteBuffer io = new WriteBuffer(payload.getLengthInBytes(), true);
+            payload.getMessageIO().serialize(io, payload);
+            return new SocketCANFrame(service.getMin() + node, io.getData());
+        } catch (ParseException e) {
+            throw new RuntimeException("Could not construct segmented frame", e);
+        }
+    }
+
+    private CANOpenService serviceId(int cobId) {
+        // form 32 bit socketcan identifier
+        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
+        if (service == null) {
+            for (CANOpenService val : CANOpenService.values()) {
+                if (val.getMin() > cobId && val.getMax() < cobId) {
+                    return val;
+                }
+            }
+        }
+        return service;
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
new file mode 100644
index 0000000..8b01c2a
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
@@ -0,0 +1,49 @@
+package org.apache.plc4x.java.can.socketcan;
+
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.api.conversation.canopen.CANConversation;
+import org.apache.plc4x.java.can.api.conversation.canopen.CANFrameBuilder;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
+
+import java.time.Duration;
+import java.util.function.BiConsumer;
+
+public class SocketCANConversation implements CANConversation<CANFrame> {
+
+    private final RequestTransactionManager tm;
+    private final ConversationContext<SocketCANFrame> context;
+
+    public SocketCANConversation(RequestTransactionManager tm, ConversationContext<SocketCANFrame> context) {
+        this.tm = tm;
+        this.context = context;
+    }
+
+    @Override
+    public CANFrameBuilder<CANFrame> frameBuilder() {
+        return new SocketCANFrameBuilder();
+    }
+
+    @Override
+    public void send(CANFrame frame, BiConsumer<RequestTransaction, SendRequestContext<CANFrame>> callback) {
+        if (frame instanceof SocketCANDelegateFrame) {
+            RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+
+            ConversationContext.SendRequestContext<CANFrame> ctx = context.sendRequest(((SocketCANDelegateFrame) frame).getFrame())
+                .expectResponse(SocketCANFrame.class, Duration.ofSeconds(10L))
+//                .onError((response, error) -> {
+//                    System.err.println("Unexpected frame " + response + " " + error);
+//                })
+                .unwrap(SocketCANDelegateFrame::new);
+            //return CompletableFuture.completedFuture(new SocketCANTransactionContext<>(transaction, ctx));
+            callback.accept(transaction, ctx);
+            return;
+        }
+        throw new PlcRuntimeException("Unsupported frame type " + frame);
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANDelegateFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANDelegateFrame.java
new file mode 100644
index 0000000..4bf323e
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANDelegateFrame.java
@@ -0,0 +1,43 @@
+package org.apache.plc4x.java.can.socketcan;
+
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+
+public class SocketCANDelegateFrame implements CANFrame {
+
+    private final SocketCANFrame frame;
+
+    public SocketCANDelegateFrame(SocketCANFrame frame) {
+        this.frame = frame;
+    }
+
+    @Override
+    public int getIdentifier() {
+        return frame.getIdentifier();
+    }
+
+    @Override
+    public boolean getExtended() {
+        return frame.getExtended();
+    }
+
+    @Override
+    public boolean getRemote() {
+        return frame.getRemote();
+    }
+
+    @Override
+    public boolean getError() {
+        return frame.getError();
+    }
+
+    @Override
+    public byte[] getData() {
+        return frame.getData();
+    }
+
+    public SocketCANFrame getFrame() {
+        return frame;
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANFrameBuilder.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANFrameBuilder.java
new file mode 100644
index 0000000..b13cd17
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANFrameBuilder.java
@@ -0,0 +1,29 @@
+package org.apache.plc4x.java.can.socketcan;
+
+import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.api.conversation.canopen.CANFrameBuilder;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+
+public class SocketCANFrameBuilder implements CANFrameBuilder<CANFrame> {
+
+    private int node;
+    private byte[] data;
+
+    @Override
+    public CANFrameBuilder<CANFrame> node(int node) {
+        this.node = node;
+        return this;
+    }
+
+    @Override
+    public CANFrameBuilder<CANFrame> data(byte[] data) {
+        this.data = data;
+        return this;
+    }
+
+    @Override
+    public CANFrame build() {
+        return new SocketCANDelegateFrame(new SocketCANFrame(node, data));
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
index 8212bea..f5550ae 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
@@ -20,6 +20,15 @@ package org.apache.plc4x.java.can;
 
 import org.apache.plc4x.java.PlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.messages.PlcWriteResponse;
+import org.apache.plc4x.java.can.context.CANOpenDriverContext;
+import org.apache.plc4x.java.can.listener.Callback;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * Here we begin .. ;-)
@@ -29,8 +38,47 @@ public class Main {
     public static void main(String[] args) throws Exception {
         PlcDriverManager driverManager = new PlcDriverManager();
 
+        CANOpenDriverContext.CALLBACK.addCallback(new Callback() {
+            @Override
+            public void receive(SocketCANFrame frame) {
+                //System.err.println("Received frame " + frame);
+            }
+        });
+
         PlcConnection connection = driverManager.getConnection("canopen:javacan://vcan0?nodeId=11");
 
+        String value = "abcdef"; //UUID.randomUUID().toString();
+        CompletableFuture<? extends PlcWriteResponse> response = connection.writeRequestBuilder()
+            .addItem("foo", "SDO:13:0x2000/0x0:VISIBLE_STRING", value)
+            .build().execute();
+
+        response.whenComplete((writeReply, writeError) -> {
+            System.out.println("====================================");
+            if (writeError != null) {
+                System.out.println("Error ");
+                writeError.printStackTrace();
+            } else {
+                System.out.println("Result " + writeReply.getResponseCode("foo") + " " + value);
+
+                PlcReadRequest.Builder builder = connection.readRequestBuilder();
+                builder.addItem("foo", "SDO:13:0x2000/0x0:VISIBLE_STRING");
+                CompletableFuture<? extends PlcReadResponse> future = builder.build().execute();
+                future.whenComplete((readReply, readError) -> {
+                    System.out.println("====================================");
+                    if (readError != null) {
+                        System.out.println("Error ");
+                        readError.printStackTrace();
+                    } else {
+                        System.out.println("Result " + readReply.getString("foo"));
+                    }
+                });
+            }
+        });
+
+
+//        while (true) {
+
+//        }
     }
 
 }
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenFieldSDOTest.java
similarity index 56%
copy from sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
copy to sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenFieldSDOTest.java
index 8212bea..a6ceae9 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenFieldSDOTest.java
@@ -16,21 +16,23 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 */
-package org.apache.plc4x.java.can;
+package org.apache.plc4x.java.can.field;
 
-import org.apache.plc4x.java.PlcDriverManager;
-import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.junit.jupiter.api.Test;
 
-/**
- * Here we begin .. ;-)
- */
-public class Main {
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
-    public static void main(String[] args) throws Exception {
-        PlcDriverManager driverManager = new PlcDriverManager();
+class CANOpenFieldSDOTest {
 
-        PlcConnection connection = driverManager.getConnection("canopen:javacan://vcan0?nodeId=11");
+    @Test
+    public void testFieldSyntax() {
+        final CANOpenSDOField canField = CANOpenSDOField.of("SDO:20:0x30/40:BOOLEAN");
 
+        assertEquals(20, canField.getNodeId());
+        assertEquals(0x30, canField.getIndex());
+        assertEquals(40, canField.getSubIndex());
+        assertEquals(CANOpenDataType.BOOLEAN, canField.getCanOpenDataType());
     }
 
-}
+}
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenNMTFieldTest.java
similarity index 50%
copy from sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
copy to sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenNMTFieldTest.java
index 214794d..3f48fc1 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenNMTFieldTest.java
@@ -16,34 +16,35 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 */
-package org.apache.plc4x.java.can.configuration;
+package org.apache.plc4x.java.can.field;
 
-import org.apache.plc4x.java.spi.configuration.Configuration;
-import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
-import org.apache.plc4x.java.transport.socketcan.CANTransportConfiguration;
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.junit.jupiter.api.Test;
 
-public class CANConfiguration implements Configuration, CANTransportConfiguration {
+import static org.junit.jupiter.api.Assertions.*;
 
-    @ConfigurationParameter
-    private int nodeId;
+class CANOpenNMTFieldTest {
 
-    @ConfigurationParameter
-    private boolean hearbeat;
+    @Test
+    public void testNodeSyntax() {
+        final CANOpenNMTField canField = CANOpenNMTField.of("NMT:20");
 
-    public int getNodeId() {
-        return nodeId;
+        assertEquals(20, canField.getNodeId());
+        assertFalse(canField.isWildcard());
     }
 
-    public void setNodeId(int nodeId) {
-        this.nodeId = nodeId;
-    }
+    @Test
+    public void testWildcardSyntax() {
+        final CANOpenNMTField canField = CANOpenNMTField.of("NMT:0");
 
-    public boolean isHeartbeat() {
-        return hearbeat;
+        assertEquals(0, canField.getNodeId());
+        assertTrue(canField.isWildcard());
     }
 
-    public void setHearbeat(boolean hearbeat) {
-        this.hearbeat = hearbeat;
+
+    @Test
+    public void testInvalidSyntax() {
+        assertThrows(PlcInvalidFieldException.class, () -> CANOpenNMTField.of("NMT:"));
     }
 
-}
+}
\ No newline at end of file


[plc4x] 19/21: Combine socketcan and canopen frames together through dedicated IO classes.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit fdd6347b6d9747e2ae0f5bf7a784ac3a4655d0c1
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Sun Oct 11 01:36:32 2020 +0200

    Combine socketcan and canopen frames together through dedicated IO classes.
    
    CANOpenFrame is interface and can be still built and parsed witohut depending on specific transport.
    Additional cleanups in conversation API.
---
 .../apache/plc4x/java/can/CANOpenPlcDriver.java    |  13 +-
 .../org/apache/plc4x/java/can/api/CANFrame.java    |  11 --
 .../apache/plc4x/java/can/api/CANOpenFrame.java    |  66 ---------
 .../api/conversation/canopen/CANConversation.java  |  16 +--
 .../api/conversation/canopen/CANFrameBuilder.java  |  11 --
 .../conversation/canopen/CANOpenConversation.java  |  68 ++-------
 .../canopen/CANOpenConversationBase.java           |   1 +
 .../api/conversation/canopen/SDOConversation.java  |  23 +--
 .../canopen/SDODownloadConversation.java           |  35 +++--
 .../canopen/SDOUploadConversation.java             |  50 ++-----
 .../plc4x/java/can/canopen/CANOpenFrame.java       |  15 ++
 .../java/can/canopen/CANOpenFrameBuilder.java      |  16 +++
 .../can/canopen/CANOpenFrameBuilderFactory.java    |   7 +
 .../canopen/socketcan/CANOpenSocketCANFrame.java   | 160 +++++++++++++++++++++
 .../socketcan/CANOpenSocketCANFrameBuilder.java    |  37 +++++
 .../socketcan/io/CANOpenSocketCANFrameIO.java      | 159 ++++++++++++++++++++
 .../java/can/protocol/CANOpenProtocolLogic.java    | 143 ++++++++----------
 .../java/can/socketcan/SocketCANConversation.java  |  39 ++---
 .../java/can/socketcan/SocketCANDelegateFrame.java |  43 ------
 .../java/can/socketcan/SocketCANFrameBuilder.java  |  29 ----
 .../apache/plc4x/java/can/CANOpenDriverSDOIT.java  |  11 ++
 .../plc4x/java/can/field/CANOpenPDOFieldTest.java  |  25 ++++
 22 files changed, 572 insertions(+), 406 deletions(-)

diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
index c34806f..732cbfb 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
@@ -19,13 +19,12 @@
 package org.apache.plc4x.java.can;
 
 import io.netty.buffer.ByteBuf;
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
+import org.apache.plc4x.java.can.canopen.socketcan.io.CANOpenSocketCANFrameIO;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.can.context.CANOpenDriverContext;
-import org.apache.plc4x.java.can.field.CANFieldHandler;
 import org.apache.plc4x.java.can.field.CANOpenFieldHandler;
 import org.apache.plc4x.java.can.protocol.CANOpenProtocolLogic;
-import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
-import org.apache.plc4x.java.socketcan.readwrite.io.SocketCANFrameIO;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
@@ -35,7 +34,7 @@ import java.util.function.ToIntFunction;
 
 /**
  */
-public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
+public class CANOpenPlcDriver extends GeneratedDriverBase<CANOpenFrame> {
 
     @Override
     public String getProtocolCode() {
@@ -44,7 +43,7 @@ public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
 
     @Override
     public String getProtocolName() {
-        return "CANopen";
+        return "CAN Open";
     }
 
     @Override
@@ -78,8 +77,8 @@ public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
     }
 
     @Override
-    protected ProtocolStackConfigurer<SocketCANFrame> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(SocketCANFrame.class, SocketCANFrameIO.class)
+    protected ProtocolStackConfigurer<CANOpenFrame> getStackConfigurer() {
+        return SingleProtocolStackConfigurer.builder(CANOpenFrame.class, CANOpenSocketCANFrameIO.class)
             .withProtocol(CANOpenProtocolLogic.class)
             .withDriverContext(CANOpenDriverContext.class)
             .withPacketSizeEstimator(CANEstimator.class)
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANFrame.java
deleted file mode 100644
index ff1cad6..0000000
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANFrame.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.apache.plc4x.java.can.api;
-
-public interface CANFrame {
-
-    int getIdentifier();
-    boolean getExtended();
-    boolean getRemote();
-    boolean getError();
-    byte[] getData();
-
-}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANOpenFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANOpenFrame.java
deleted file mode 100644
index 27db3c2..0000000
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/CANOpenFrame.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.apache.plc4x.java.can.api;
-
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
-import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
-import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
-import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
-import org.apache.plc4x.java.spi.generation.ParseException;
-import org.apache.plc4x.java.spi.generation.ReadBuffer;
-
-public class CANOpenFrame implements CANFrame {
-
-    private final CANFrame frame;
-
-    private final CANOpenPayload payload;
-
-    public CANOpenFrame(CANFrame frame) {
-        this.frame = frame;
-        try {
-            this.payload = CANOpenPayloadIO.staticParse(new ReadBuffer(frame.getData(), true), serviceId(frame.getIdentifier()));
-        } catch (ParseException e) {
-            throw new PlcRuntimeException("Could not parse CANopen payload", e);
-        }
-    }
-
-    public CANOpenPayload getPayload() {
-        return payload;
-    }
-
-    @Override
-    public int getIdentifier() {
-        return frame.getIdentifier();
-    }
-
-    @Override
-    public boolean getExtended() {
-        return frame.getExtended();
-    }
-
-    @Override
-    public boolean getRemote() {
-        return frame.getRemote();
-    }
-
-    @Override
-    public boolean getError() {
-        return frame.getError();
-    }
-
-    @Override
-    public byte[] getData() {
-        return frame.getData();
-    }
-
-    private CANOpenService serviceId(int cobId) {
-        // form 32 bit socketcan identifier
-        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
-        if (service == null) {
-            for (CANOpenService val : CANOpenService.values()) {
-                if (val.getMin() > cobId && val.getMax() < cobId) {
-                    return val;
-                }
-            }
-        }
-        return service;
-    }
-}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
index be4675b..471e8a6 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANConversation.java
@@ -1,20 +1,18 @@
 package org.apache.plc4x.java.can.api.conversation.canopen;
 
-import org.apache.plc4x.java.can.api.CANFrame;
-
-import java.util.function.BiConsumer;
-
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
+import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilder;
 import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
-import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
 
-public interface CANConversation<W extends CANFrame> {
+import java.util.function.Consumer;
 
-    int getNodeId();
+public interface CANConversation<W extends CANOpenFrame> {
 
-    CANFrameBuilder<W> frameBuilder();
+    int getNodeId();
 
-    void send(RequestTransaction transaction, W frame, BiConsumer<RequestTransaction, SendRequestContext<W>> callback);
+    CANOpenFrameBuilder createBuilder();
 
+    void send(W frame, Consumer<SendRequestContext<W>> callback);
 
 }
 
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANFrameBuilder.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANFrameBuilder.java
deleted file mode 100644
index 53a68c3..0000000
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANFrameBuilder.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.apache.plc4x.java.can.api.conversation.canopen;
-
-public interface CANFrameBuilder<W> {
-
-    CANFrameBuilder<W> node(int node);
-
-    CANFrameBuilder<W> data(byte[] data);
-
-    W build();
-
-}
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
index fee418d..9785fee 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversation.java
@@ -1,85 +1,41 @@
 package org.apache.plc4x.java.can.api.conversation.canopen;
 
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
-import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
-import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
-import org.apache.plc4x.java.spi.generation.ParseException;
-import org.apache.plc4x.java.spi.generation.ReadBuffer;
-import org.apache.plc4x.java.spi.generation.WriteBuffer;
-import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
-import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.concurrent.CompletableFuture;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
-public class CANOpenConversation<W extends CANFrame> {
+public class CANOpenConversation {
 
     private final Logger logger = LoggerFactory.getLogger(CANOpenConversation.class);
-    private final RequestTransaction transaction;
     private final int node;
-    private final CANConversation<W> delegate;
+    private final CANConversation<CANOpenFrame> delegate;
 
-    public CANOpenConversation(RequestTransaction transaction, int node, CANConversation<W> delegate) {
-        this.transaction = transaction;
+    public CANOpenConversation(int node, CANConversation<CANOpenFrame> delegate) {
         this.node = node;
         this.delegate = delegate;
     }
 
-    public SDOConversation<W> sdo() {
-        return new SDOConversation<>(this);
+    public SDOConversation sdo() {
+        return new SDOConversation(this);
     }
 
-    public void send(CANOpenService service, CANOpenPayload payload, BiConsumer<RequestTransaction, SendRequestContext<CANOpenPayload>> callback) {
-        CANFrameBuilder<W> builder = delegate.frameBuilder();
-        W frame = builder.node(service.getMin() + node).data(serialize(payload)).build();
-        logger.info("Request data under transaction {}", transaction);
-        delegate.send(transaction, frame, (tx, ctx) -> {
+    public void send(CANOpenService service, CANOpenPayload payload, Consumer<SendRequestContext<CANOpenPayload>> callback) {
+        CANOpenFrame frame = delegate.createBuilder().withNodeId(node).withService(service).withPayload(payload).build();
+        delegate.send(frame, (ctx) -> {
             SendRequestContext<CANOpenPayload> unwrap = ctx
 //                .onError((response, error) -> {
 //                    System.err.println("Unexpected frame " + response + " " + error);
 //                })
-            .unwrap(CANOpenConversation.this::deserialize);
-            callback.accept(tx, unwrap);
+            .unwrap(CANOpenFrame::getPayload);
+            callback.accept(unwrap);
         });
 
 
     }
 
-    private CANOpenPayload deserialize(CANFrame frame) {
-        try {
-            CANOpenService service = serviceId(frame.getIdentifier());
-            ReadBuffer buffer = new ReadBuffer(frame.getData(), true);
-            return CANOpenPayloadIO.staticParse(buffer, service);
-        } catch (ParseException e) {
-            throw new PlcRuntimeException("Could not deserialize CAN open payload", e);
-        }
-    }
-
-    private byte[] serialize(CANOpenPayload payload) {
-        try {
-            WriteBuffer buffer = new WriteBuffer(payload.getLengthInBytes(), true);
-            CANOpenPayloadIO.staticSerialize(buffer, payload);
-            return buffer.getData();
-        } catch (ParseException e) {
-            throw new PlcRuntimeException("Could not serialize CAN open payload", e);
-        }
-    }
-
-    private CANOpenService serviceId(int cobId) {
-        // form 32 bit socketcan identifier
-        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
-        if (service == null) {
-            for (CANOpenService val : CANOpenService.values()) {
-                if (val.getMin() > cobId && val.getMax() < cobId) {
-                    return val;
-                }
-            }
-        }
-        return service;
-    }
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversationBase.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversationBase.java
index 60b87e4..0c65988 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversationBase.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/CANOpenConversationBase.java
@@ -1,6 +1,7 @@
 package org.apache.plc4x.java.can.api.conversation.canopen;
 
 import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
 import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
 import org.apache.plc4x.java.spi.generation.ParseException;
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOConversation.java
index 1447f08..251e4da 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOConversation.java
@@ -1,7 +1,7 @@
 package org.apache.plc4x.java.can.api.conversation.canopen;
 
 import org.apache.plc4x.java.api.value.PlcValue;
-import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse;
 import org.apache.plc4x.java.canopen.readwrite.IndexAddress;
@@ -12,31 +12,32 @@ import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
 
 import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
-public class SDOConversation<W extends CANFrame> {
+public class SDOConversation {
 
-    private final CANOpenConversation<W> delegate;
+    private final CANOpenConversation delegate;
 
-    public SDOConversation(CANOpenConversation<W> delegate) {
+    public SDOConversation(CANOpenConversation delegate) {
         this.delegate = delegate;
     }
 
-    public SDODownloadConversation<W> download(IndexAddress indexAddress, PlcValue value, CANOpenDataType type) {
-        return new SDODownloadConversation<>(this, indexAddress, value, type);
+    public SDODownloadConversation download(IndexAddress indexAddress, PlcValue value, CANOpenDataType type) {
+        return new SDODownloadConversation(this, indexAddress, value, type);
     }
 
-    public SDOUploadConversation<W> upload(IndexAddress indexAddress, CANOpenDataType type) {
-        return new SDOUploadConversation<>(this, indexAddress, type);
+    public SDOUploadConversation upload(IndexAddress indexAddress, CANOpenDataType type) {
+        return new SDOUploadConversation(this, indexAddress, type);
     }
 
-    public void send(SDORequest request, BiConsumer<RequestTransaction, SendRequestContext<CANOpenSDOResponse>> callback) {
-        delegate.send(CANOpenService.RECEIVE_SDO, new CANOpenSDORequest(request.getCommand(), request), (tx, ctx) -> {
+    public void send(SDORequest request, Consumer<SendRequestContext<CANOpenSDOResponse>> callback) {
+        delegate.send(CANOpenService.RECEIVE_SDO, new CANOpenSDORequest(request.getCommand(), request), (ctx) -> {
             SendRequestContext<CANOpenSDOResponse> context = ctx
 //            .onError((response, error) -> {
 //                System.out.println("Unexpected frame " + response + " " + error);
 //            })
             .only(CANOpenSDOResponse.class);
-            callback.accept(tx, context);
+            callback.accept(context);
         });
     }
 
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDODownloadConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDODownloadConversation.java
index 470e190..8e90bb6 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDODownloadConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDODownloadConversation.java
@@ -4,26 +4,22 @@ import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.api.value.PlcValue;
-import org.apache.plc4x.java.can.api.CANFrame;
-import org.apache.plc4x.java.can.api.segmentation.accumulator.ByteStorage;
 import org.apache.plc4x.java.canopen.readwrite.*;
 import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
 import org.apache.plc4x.java.canopen.readwrite.types.SDOResponseCommand;
 import org.apache.plc4x.java.spi.generation.ParseException;
 
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.CompletableFuture;
 import java.util.function.BiConsumer;
 
-public class SDODownloadConversation<W extends CANFrame> {
+public class SDODownloadConversation extends CANOpenConversationBase {
 
-    private final SDOConversation<W> delegate;
+    private final SDOConversation delegate;
     private final IndexAddress indexAddress;
     private final byte[] data;
 
-    public SDODownloadConversation(SDOConversation<W> delegate, IndexAddress indexAddress, PlcValue value, CANOpenDataType type) {
+    public SDODownloadConversation(SDOConversation delegate, IndexAddress indexAddress, PlcValue value, CANOpenDataType type) {
         this.delegate = delegate;
         this.indexAddress = indexAddress;
 
@@ -34,12 +30,12 @@ public class SDODownloadConversation<W extends CANFrame> {
         }
     }
 
-    public void execute(BiConsumer<PlcResponseCode, Throwable> receiver) throws PlcException {
+    public void execute(CompletableFuture<PlcResponseCode> receiver) {
         if (data.length > 4) {
             // segmented
 
             SDOInitiateSegmentedUploadResponse size = new SDOInitiateSegmentedUploadResponse(data.length);
-            delegate.send(new SDOInitiateDownloadRequest(false, true, indexAddress, size), (tx, ctx) -> {
+            delegate.send(new SDOInitiateDownloadRequest(false, true, indexAddress, size), (ctx) -> {
                 ctx.unwrap(CANOpenSDOResponse::getResponse)
                     .check(p -> p.getCommand() == SDOResponseCommand.INITIATE_DOWNLOAD)
                     .only(SDOInitiateDownloadResponse.class)
@@ -59,11 +55,12 @@ public class SDODownloadConversation<W extends CANFrame> {
             new SDOInitiateExpeditedUploadResponse(data)
         );
 
-        delegate.send(rq, (tx, ctx) ->
+        delegate.send(rq, (ctx) ->
             ctx.onError((response, error) -> {
                 System.out.println("Unexpected frame " + response + " " + error);
             })
             .unwrap(CANOpenSDOResponse::getResponse)
+            .only(SDOInitiateDownloadResponse.class)
             .check(r -> r.getCommand() == SDOResponseCommand.INITIATE_DOWNLOAD)
             .handle(r -> {
                 System.out.println(r);
@@ -71,23 +68,25 @@ public class SDODownloadConversation<W extends CANFrame> {
         );
     }
 
-    private void put(byte[] data, BiConsumer<PlcResponseCode, Throwable> receiver, boolean toggle, int offset) {
+    private void put(byte[] data, CompletableFuture<PlcResponseCode> receiver, boolean toggle, int offset) {
         int remaining = data.length - offset;
         byte[] segment = new byte[Math.min(remaining, 7)];
         System.arraycopy(data, offset, segment, 0, segment.length);
 
-        delegate.send(new SDOSegmentDownloadRequest(toggle, remaining <= 7, segment), (tx, ctx) -> {
+        delegate.send(new SDOSegmentDownloadRequest(toggle, remaining <= 7, segment), (ctx) -> {
             ctx.unwrap(CANOpenSDOResponse::getResponse)
                 .only(SDOSegmentDownloadResponse.class)
                 .onError((response, error) -> {
-                    System.out.println("Unexpected frame " + response + " " + error);
-                    receiver.accept(null, error);
+                    if (error != null) {
+                        receiver.completeExceptionally(error);
+                    } else {
+                        receiver.completeExceptionally(new PlcException("Transaction terminated"));
+                    }
                 })
-                .check(r -> r.getToggle() == toggle)
+                .check(response -> response.getToggle() == toggle)
                 .handle(reply -> {
                     if (offset + segment.length == data.length) {
-                        // validate offset
-                        receiver.accept(PlcResponseCode.OK, null);
+                        receiver.complete(PlcResponseCode.OK);
                     } else {
                         put(data, receiver, !toggle, offset + segment.length);
                     }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
index 0bb0998..0beb17c 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/conversation/canopen/SDOUploadConversation.java
@@ -2,37 +2,34 @@ package org.apache.plc4x.java.can.api.conversation.canopen;
 
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.value.PlcValue;
-import org.apache.plc4x.java.can.api.CANFrame;
 import org.apache.plc4x.java.can.api.segmentation.accumulator.ByteStorage;
 import org.apache.plc4x.java.canopen.readwrite.*;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
 import org.apache.plc4x.java.spi.generation.ParseException;
-import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.function.BiConsumer;
 
-public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversationBase {
+public class SDOUploadConversation extends CANOpenConversationBase {
 
     private final Logger logger = LoggerFactory.getLogger(SDOUploadConversation.class);
-    private final SDOConversation<W> delegate;
+    private final SDOConversation delegate;
     private final IndexAddress address;
     private final CANOpenDataType type;
 
-    public SDOUploadConversation(SDOConversation<W> delegate, IndexAddress address, CANOpenDataType type) {
+    public SDOUploadConversation(SDOConversation delegate, IndexAddress address, CANOpenDataType type) {
         this.delegate = delegate;
         this.address = address;
         this.type = type;
     }
 
-    public void execute(CompletableFuture<PlcValue> receiver) throws PlcException {
+    public void execute(CompletableFuture<PlcValue> receiver) {
         SDOInitiateUploadRequest rq = new SDOInitiateUploadRequest(address);
 
-        delegate.send(rq, (tx, ctx) ->
+        delegate.send(rq, (ctx) ->
             ctx.onError((response, error) -> {
-                System.err.println("Unexpected frame " + response + " " + error);
                 if (error != null) {
                     receiver.completeExceptionally(error);
                     return;
@@ -43,34 +40,16 @@ public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversati
                     receiver.completeExceptionally(new PlcException("Could not read value. Remote party reported code " + sdoAbort.getCode()));
                 }
             })
-            .check(reply -> {
-                logger.warn("Received answer {}", reply);
-                return true;
-            })
-            .unwrap(CANOpenSDOResponse::getResponse).check(reply -> {
-                logger.warn("Received answer {}", reply);
-                return true;
-            })
-            .check(reply -> {
-                logger.warn("Received answer {}", reply);
-                return true;
-            })
+            .unwrap(CANOpenSDOResponse::getResponse)
             .only(SDOInitiateUploadResponse.class)
-            .check(resp -> {
-                logger.warn("Checking if reply address {}/{} matches {}/{}: {}",
-                    Integer.toHexString(resp.getAddress().getIndex()), Integer.toHexString(resp.getAddress().getSubindex()),
-                    Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()),
-                    resp.getAddress().equals(address)
-                );
-                return resp.getAddress().equals(address);
-            })
+            .check(response -> response.getAddress().equals(address))
             .handle(response -> {
-                handle(tx, receiver, response);
+                handle(receiver, response);
             })
         );
     }
 
-    private void handle(RequestTransactionManager.RequestTransaction tx, CompletableFuture<PlcValue> receiver, SDOInitiateUploadResponse answer) {
+    private void handle(CompletableFuture<PlcValue> receiver, SDOInitiateUploadResponse answer) {
         BiConsumer<Integer, byte[]> valueCallback = (length, bytes) -> {
             try {
                 final PlcValue decodedValue = decodeFrom(bytes, type, length);
@@ -84,7 +63,7 @@ public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversati
             SDOInitiateExpeditedUploadResponse payload = (SDOInitiateExpeditedUploadResponse) answer.getPayload();
             valueCallback.accept(payload.getData().length, payload.getData());
         } else if (answer.getPayload() instanceof SDOInitiateSegmentedUploadResponse) {
-            logger.info("Beginning of segmented operation for address {}/{}", Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()));
+            logger.debug("Beginning of segmented operation for address {}/{}", Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()));
             ByteStorage.SDOUploadStorage storage = new ByteStorage.SDOUploadStorage();
             storage.append(answer);
 
@@ -97,10 +76,9 @@ public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversati
 
     private void fetch(ByteStorage.SDOUploadStorage storage, BiConsumer<Integer, byte[]> valueCallback, CompletableFuture<PlcValue> receiver, boolean toggle, int size) {
         logger.info("Request next data block for address {}/{}", Integer.toHexString(address.getIndex()), Integer.toHexString(address.getSubindex()));
-        delegate.send(new SDOSegmentUploadRequest(toggle), (tx, ctx) -> {
+        delegate.send(new SDOSegmentUploadRequest(toggle), (ctx) -> {
             ctx.unwrap(CANOpenSDOResponse::getResponse)
                 .onError((response, error) -> {
-                    System.out.println("Unexpected frame " + response + " " + error);
                     if (error != null) {
                         receiver.completeExceptionally(error);
                         return;
@@ -115,12 +93,6 @@ public class SDOUploadConversation<W extends CANFrame> extends CANOpenConversati
                 .only(SDOSegmentUploadResponse.class)
                 .check(r -> r.getToggle() == toggle)
                 .handle(response -> {
-//                    if (!reply.getToggle() == toggle) { // toggle flag is wrong, abort transaction
-//                        logger.info("Received invalid answer from party for {}", address);
-//                        delegate.send(new SDOAbortRequest(new SDOAbort(address, 0x100)), (tx2, ctx2) -> {});
-//                        return;
-//                    }
-
                     storage.append(response);
 
                     if (response.getLast()) {
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrame.java
new file mode 100644
index 0000000..2a32bda
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrame.java
@@ -0,0 +1,15 @@
+package org.apache.plc4x.java.can.canopen;
+
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.spi.generation.Message;
+
+public interface CANOpenFrame extends Message {
+
+    int getNodeId();
+
+    CANOpenService getService();
+
+    CANOpenPayload getPayload();
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrameBuilder.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrameBuilder.java
new file mode 100644
index 0000000..17235b6
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrameBuilder.java
@@ -0,0 +1,16 @@
+package org.apache.plc4x.java.can.canopen;
+
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+
+public interface CANOpenFrameBuilder {
+
+    CANOpenFrameBuilder withNodeId(int node);
+
+    CANOpenFrameBuilder withService(CANOpenService service);
+
+    CANOpenFrameBuilder withPayload(CANOpenPayload payload);
+
+    CANOpenFrame build();
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrameBuilderFactory.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrameBuilderFactory.java
new file mode 100644
index 0000000..9704f6c
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/CANOpenFrameBuilderFactory.java
@@ -0,0 +1,7 @@
+package org.apache.plc4x.java.can.canopen;
+
+public interface CANOpenFrameBuilderFactory {
+
+    CANOpenFrameBuilder createBuilder();
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/CANOpenSocketCANFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/CANOpenSocketCANFrame.java
new file mode 100644
index 0000000..d51f042
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/CANOpenSocketCANFrame.java
@@ -0,0 +1,160 @@
+/*
+  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.can.canopen.socketcan;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.spi.generation.MessageIO;
+import org.apache.plc4x.java.can.canopen.socketcan.io.CANOpenSocketCANFrameIO;
+
+import java.util.Objects;
+
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
+public class CANOpenSocketCANFrame implements CANOpenFrame {
+
+    // Properties.
+    private final int nodeId;
+    private final CANOpenService service;
+    private final CANOpenPayload payload;
+
+    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
+    public CANOpenSocketCANFrame(@JsonProperty("nodeId") int nodeId, @JsonProperty("service") CANOpenService service, @JsonProperty("payload") CANOpenPayload payload) {
+        this.nodeId = nodeId;
+        this.service = service;
+        this.payload = payload;
+    }
+
+    public CANOpenSocketCANFrame(int nodeId, CANOpenPayload payload) {
+        this(nodeId, payload.getFunction(), payload);
+    }
+
+    @Override
+    public int getNodeId() {
+        return nodeId;
+    }
+
+    @Override
+    public CANOpenService getService() {
+        return service;
+    }
+
+    @Override
+    public CANOpenPayload getPayload() {
+        return payload;
+    }
+
+    @Override
+    @JsonIgnore
+    public int getLengthInBytes() {
+        return getLengthInBits() / 8;
+    }
+
+    @Override
+    @JsonIgnore
+    public int getLengthInBits() {
+        int lengthInBits = 0;
+
+        // Simple field (node + service)
+        lengthInBits += 32;
+
+        // A virtual field doesn't have any in- or output.
+
+        // A virtual field doesn't have any in- or output.
+
+        // A virtual field doesn't have any in- or output.
+
+        // A virtual field doesn't have any in- or output.
+
+        // Implicit Field (size)
+        lengthInBits += 8;
+
+        // Reserved Field (reserved)
+        lengthInBits += 8;
+
+        // Reserved Field (reserved)
+        lengthInBits += 8;
+
+        // Reserved Field (reserved)
+        lengthInBits += 8;
+
+        // Array field
+        if (payload != null) {
+            lengthInBits += 8 * payload.getLengthInBytes();
+        }
+
+        // Padding Field (padding)
+        int _timesPadding = (8) - payload.getLengthInBytes();
+        while (_timesPadding-- > 0) {
+            lengthInBits += 8;
+        }
+
+        return lengthInBits;
+    }
+
+    @Override
+    @JsonIgnore
+    public MessageIO<CANOpenFrame, CANOpenFrame> getMessageIO() {
+        return new CANOpenSocketCANFrameIO();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof CANOpenSocketCANFrame)) {
+            return false;
+        }
+        CANOpenSocketCANFrame that = (CANOpenSocketCANFrame) o;
+        return (getNodeId() == that.getNodeId()) &&
+            (getService() == that.getService()) &&
+            (getPayload() == that.getPayload());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+            getNodeId(),
+            getService(),
+            getPayload()
+        );
+    }
+
+    @Override
+    public String toString() {
+        return toString(ToStringStyle.SHORT_PREFIX_STYLE);
+    }
+
+    public String toString(ToStringStyle style) {
+        return new ToStringBuilder(this, style)
+            .append("nodeId", getNodeId())
+            .append("service", getService())
+            .append("payload", getPayload())
+            .toString();
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/CANOpenSocketCANFrameBuilder.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/CANOpenSocketCANFrameBuilder.java
new file mode 100644
index 0000000..87f7b2f
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/CANOpenSocketCANFrameBuilder.java
@@ -0,0 +1,37 @@
+package org.apache.plc4x.java.can.canopen.socketcan;
+
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
+import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilder;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+
+public class CANOpenSocketCANFrameBuilder implements CANOpenFrameBuilder {
+
+    private int node;
+    private CANOpenPayload payload;
+    private CANOpenService service;
+
+    @Override
+    public CANOpenFrameBuilder withNodeId(int node) {
+        this.node = node;
+        return this;
+    }
+
+    @Override
+    public CANOpenFrameBuilder withService(CANOpenService service) {
+        this.service = service;
+        return this;
+    }
+
+    @Override
+    public CANOpenFrameBuilder withPayload(CANOpenPayload payload) {
+        this.payload = payload;
+        return this;
+    }
+
+    @Override
+    public CANOpenFrame build() {
+        return new CANOpenSocketCANFrame(node, service, payload);
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/io/CANOpenSocketCANFrameIO.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/io/CANOpenSocketCANFrameIO.java
new file mode 100644
index 0000000..1b6dbf2
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/canopen/socketcan/io/CANOpenSocketCANFrameIO.java
@@ -0,0 +1,159 @@
+/*
+  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.can.canopen.socketcan.io;
+
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
+import org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.spi.generation.MessageIO;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CANOpenSocketCANFrameIO implements MessageIO<CANOpenFrame, CANOpenFrame> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(CANOpenSocketCANFrameIO.class);
+
+    @Override
+    public CANOpenSocketCANFrame parse(ReadBuffer io, Object... args) throws ParseException {
+        return CANOpenSocketCANFrameIO.staticParse(io);
+    }
+
+    @Override
+    public void serialize(WriteBuffer io, CANOpenFrame value, Object... args) throws ParseException {
+        CANOpenSocketCANFrameIO.staticSerialize(io, (CANOpenSocketCANFrame) value);
+    }
+
+    public static CANOpenSocketCANFrame staticParse(ReadBuffer io) throws ParseException {
+        int startPos = io.getPos();
+        int curPos;
+
+        // Simple Field (rawId)
+        int rawId = io.readInt(32);
+
+        // Virtual field (Just declare a local variable so we can access it in the parser)
+        int identifier = (int) (org.apache.plc4x.java.can.helper.HeaderParser.readIdentifier(rawId));
+
+        CANOpenService service = serviceId(identifier);
+        int nodeId = Math.abs(service.getMin() - identifier);
+
+        // Implicit Field (size) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+        short size = io.readUnsignedShort(8);
+
+        // Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+        {
+            short reserved = io.readUnsignedShort(8);
+            if(reserved != (short) 0x0) {
+                LOGGER.info("Expected constant value " + 0x0 + " but got " + reserved + " for reserved field.");
+            }
+        }
+
+        // Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+        {
+            short reserved = io.readUnsignedShort(8);
+            if(reserved != (short) 0x0) {
+                LOGGER.info("Expected constant value " + 0x0 + " but got " + reserved + " for reserved field.");
+            }
+        }
+
+        // Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+        {
+            short reserved = io.readUnsignedShort(8);
+            if(reserved != (short) 0x0) {
+                LOGGER.info("Expected constant value " + 0x0 + " but got " + reserved + " for reserved field.");
+            }
+        }
+
+        // Array field (data)
+        // Count array
+        if(size > Integer.MAX_VALUE) {
+            throw new ParseException("Array count of " + (size) + " exceeds the maximum allowed count of " + Integer.MAX_VALUE);
+        }
+
+        final CANOpenPayload payload = CANOpenPayloadIO.staticParse(io, service);
+
+        // Padding Field (padding)
+        {
+            int _timesPadding = (int) ((8) - payload.getLengthInBytes());
+            while ((io.hasMore(8)) && (_timesPadding-- > 0)) {
+                // Just read the padding data and ignore it
+                io.readUnsignedShort(8);
+            }
+        }
+
+        // Create the instance
+        return new CANOpenSocketCANFrame(nodeId, service, payload);
+    }
+
+    public static void staticSerialize(WriteBuffer io, CANOpenSocketCANFrame _value) throws ParseException {
+        int startPos = io.getPos();
+
+        // Simple Field (service)
+        int nodeId = _value.getNodeId();
+        int service = _value.getService().getMin();
+        io.writeInt(32, (service + nodeId));
+
+        // Implicit Field (size) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+        final CANOpenPayload payload = _value.getPayload();
+        short size = (short) (payload == null ? 0 : payload.getLengthInBytes());
+        io.writeUnsignedShort(8, ((Number) (size)).shortValue());
+
+        // Reserved Field (reserved)
+        io.writeUnsignedShort(8, ((Number) (short) 0x0).shortValue());
+
+        // Reserved Field (reserved)
+        io.writeUnsignedShort(8, ((Number) (short) 0x0).shortValue());
+
+        // Reserved Field (reserved)
+        io.writeUnsignedShort(8, ((Number) (short) 0x0).shortValue());
+
+        // Array Field (data)
+        if(_value.getPayload() != null) {
+            payload.getMessageIO().serialize(io, payload);
+        }
+
+        // Padding Field (padding)
+        {
+            int _timesPadding = (int) ((8) - size);
+            while (_timesPadding-- > 0) {
+                short _paddingValue = (short) (0);
+                io.writeUnsignedShort(8, ((Number) (_paddingValue)).shortValue());
+            }
+        }
+    }
+
+    public static CANOpenService serviceId(int cobId) {
+        // form 32 bit socketcan identifier
+        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
+        if (service == null) {
+            for (CANOpenService val : CANOpenService.values()) {
+                if (val.getMin() > cobId && val.getMax() < cobId) {
+                    return val;
+                }
+            }
+        }
+        return service;
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index e384d0a..96488ec 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -19,6 +19,7 @@ under the License.
 package org.apache.plc4x.java.can.protocol;
 
 import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
@@ -29,22 +30,21 @@ import org.apache.plc4x.java.api.types.PlcSubscriptionType;
 import org.apache.plc4x.java.api.value.PlcList;
 import org.apache.plc4x.java.api.value.PlcNull;
 import org.apache.plc4x.java.api.value.PlcValue;
-import org.apache.plc4x.java.can.api.CANFrame;
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
 import org.apache.plc4x.java.can.api.conversation.canopen.CANConversation;
 import org.apache.plc4x.java.can.api.conversation.canopen.CANOpenConversation;
 import org.apache.plc4x.java.can.api.conversation.canopen.SDODownloadConversation;
 import org.apache.plc4x.java.can.api.conversation.canopen.SDOUploadConversation;
+import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilder;
+import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilderFactory;
+import org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrameBuilder;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.can.context.CANOpenDriverContext;
 import org.apache.plc4x.java.can.field.CANOpenField;
 import org.apache.plc4x.java.can.field.CANOpenPDOField;
 import org.apache.plc4x.java.can.field.CANOpenSDOField;
 import org.apache.plc4x.java.can.socketcan.SocketCANConversation;
-import org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload;
-import org.apache.plc4x.java.canopen.readwrite.CANOpenPDOPayload;
-import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
-import org.apache.plc4x.java.canopen.readwrite.IndexAddress;
-import org.apache.plc4x.java.canopen.readwrite.io.CANOpenHeartbeatPayloadIO;
+import org.apache.plc4x.java.canopen.readwrite.*;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
@@ -66,8 +66,6 @@ import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Collection;
@@ -82,16 +80,18 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
-public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> implements HasConfiguration<CANConfiguration>, PlcSubscriber {
+public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implements HasConfiguration<CANConfiguration>, PlcSubscriber {
 
     private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10L);
     private Logger logger = LoggerFactory.getLogger(CANOpenProtocolLogic.class);
 
+    private CANOpenFrameBuilderFactory factory = CANOpenSocketCANFrameBuilder::new;
+
     private CANConfiguration configuration;
     private RequestTransactionManager tm;
     private Timer heartbeat;
     private CANOpenDriverContext canContext;
-    private CANConversation<CANFrame> conversation;
+    private CANConversation<CANOpenFrame> conversation;
 
     private Map<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> consumers = new ConcurrentHashMap<>();
 
@@ -114,7 +114,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     @Override
-    public void onConnect(ConversationContext<SocketCANFrame> context) {
+    public void onConnect(ConversationContext<CANOpenFrame> context) {
         try {
             if (configuration.isHeartbeat()) {
                 context.sendToWire(createFrame(new CANOpenHeartbeatPayload(NMTState.BOOTED_UP)));
@@ -138,15 +138,13 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     @Override
-    public void setContext(ConversationContext<SocketCANFrame> context) {
+    public void setContext(ConversationContext<CANOpenFrame> context) {
         super.setContext(context);
-        this.conversation = new SocketCANConversation(configuration.getNodeId(), context);
+        this.conversation = new SocketCANConversation(configuration.getNodeId(), context, factory);
     }
 
-    private SocketCANFrame createFrame(CANOpenHeartbeatPayload state) throws ParseException {
-        WriteBuffer buffer = new WriteBuffer(state.getLengthInBytes(), true);
-        CANOpenHeartbeatPayloadIO.staticSerialize(buffer, state);
-        return new SocketCANFrame(cobId(configuration.getNodeId(), CANOpenService.HEARTBEAT), buffer.getData());
+    private CANOpenFrame createFrame(CANOpenHeartbeatPayload state) throws ParseException {
+        return factory.createBuilder().withNodeId(configuration.getNodeId()).withService(CANOpenService.HEARTBEAT).withPayload(state).build();
     }
 
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
@@ -177,20 +175,24 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
     private void writeInternally(InternalPlcWriteRequest writeRequest, CANOpenSDOField field, CompletableFuture<PlcWriteResponse> response) {
         final RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
-        CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(transaction, field.getNodeId(), conversation);
 
-        PlcValue writeValue = writeRequest.getPlcValues().get(0);
+        String fieldName = writeRequest.getFieldNames().iterator().next();
 
-        SDODownloadConversation<CANFrame> download = canopen.sdo().download(new IndexAddress(field.getIndex(), field.getSubIndex()), writeValue, field.getCanOpenDataType());
-        try {
-            download.execute((value, error) -> {
-                String fieldName = writeRequest.getFieldNames().iterator().next();
-                response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.OK)));
+        CompletableFuture<PlcResponseCode> callback = new CompletableFuture<>();
+        callback.whenComplete((code, error) -> {
+            if (error != null) {
+                response.completeExceptionally(error);
                 transaction.endRequest();
-            });
-        } catch (Exception e) {
-            response.completeExceptionally(e);
-        }
+                return;
+            }
+            response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, code)));
+            transaction.endRequest();
+        });
+
+        PlcValue writeValue = writeRequest.getPlcValues().get(0);
+        CANOpenConversation canopen = new CANOpenConversation(field.getNodeId(), conversation);
+        SDODownloadConversation download = canopen.sdo().download(new IndexAddress(field.getIndex(), field.getSubIndex()), writeValue, field.getCanOpenDataType());
+        transaction.submit(() -> download.execute(callback));
     }
 
     private void writeInternally(InternalPlcWriteRequest writeRequest, CANOpenPDOField field, CompletableFuture<PlcWriteResponse> response) {
@@ -200,8 +202,13 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
             String fieldName = writeRequest.getFieldNames().iterator().next();
             WriteBuffer buffer = DataItemIO.staticSerialize(writeValue, field.getCanOpenDataType(), writeValue.getLength(), true);
             if (buffer != null) {
-                int cob = field.getService().getMin() + field.getNodeId();
-                context.sendToWire(new SocketCANFrame(cob, buffer.getData()));
+                final CANOpenPDOPayload payload = new CANOpenPDOPayload(new CANOpenPDO(buffer.getData()));
+                context.sendToWire(factory.createBuilder()
+                    .withNodeId(field.getNodeId())
+                    .withService(field.getService())
+                    .withPayload(payload)
+                    .build()
+                );
                 response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.OK)));
             } else {
                 response.complete(new DefaultPlcWriteResponse(writeRequest, Collections.singletonMap(fieldName, PlcResponseCode.INVALID_DATA)));
@@ -237,8 +244,6 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest request) {
         InternalPlcSubscriptionRequest rq = (InternalPlcSubscriptionRequest) request;
 
-        List<SubscriptionPlcField> fields = rq.getSubscriptionFields();
-
         Map<String, ResponseItem<PlcSubscriptionHandle>> answers = new LinkedHashMap<>();
         DefaultPlcSubscriptionResponse response = new DefaultPlcSubscriptionResponse(rq, answers);
 
@@ -259,42 +264,35 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     private void readInternally(InternalPlcReadRequest readRequest, CANOpenSDOField field, CompletableFuture<PlcReadResponse> response) {
-        try {
-            final RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
-            CANOpenConversation<CANFrame> canopen = new CANOpenConversation<>(transaction, field.getNodeId(), conversation);
-            System.out.println("----> Submit read " + field.getIndex() + "/" + field.getSubIndex() + " from " + field.getNodeId() + " " + transaction);
-            SDOUploadConversation<CANFrame> upload = canopen.sdo().upload(new IndexAddress(field.getIndex(), field.getSubIndex()), field.getCanOpenDataType());
-            CompletableFuture<PlcValue> callback = new CompletableFuture<>();
-            callback.whenComplete((value, error) -> {
-                System.out.println("<---- Received reply " + field.getIndex() + "/" + field.getSubIndex() + " from " + field.getNodeId() + " " + value + "/" + error + " " + transaction);
-                if (error != null) {
-                    response.completeExceptionally(error);
-                    transaction.endRequest();
-                    return;
-                }
+        String fieldName = readRequest.getFieldNames().iterator().next();
 
-                String fieldName = readRequest.getFieldNames().iterator().next();
-                Map<String, ResponseItem<PlcValue>> fields = new HashMap<>();
-                fields.put(fieldName, new ResponseItem<>(PlcResponseCode.OK, value));
-                response.complete(new DefaultPlcReadResponse(readRequest, fields));
+        final RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        CompletableFuture<PlcValue> callback = new CompletableFuture<>();
+        callback.whenComplete((value, error) -> {
+            if (error != null) {
+                response.completeExceptionally(error);
                 transaction.endRequest();
-            });
-            upload.execute(callback);
-        } catch (Exception e) {
-            response.completeExceptionally(e);
-        }
+                return;
+            }
+
+            Map<String, ResponseItem<PlcValue>> fields = new HashMap<>();
+            fields.put(fieldName, new ResponseItem<>(PlcResponseCode.OK, value));
+            response.complete(new DefaultPlcReadResponse(readRequest, fields));
+            transaction.endRequest();
+        });
+
+        CANOpenConversation canopen = new CANOpenConversation(field.getNodeId(), conversation);
+        SDOUploadConversation upload = canopen.sdo().upload(new IndexAddress(field.getIndex(), field.getSubIndex()), field.getCanOpenDataType());
+        transaction.submit(() -> upload.execute(callback));
     }
 
     @Override
-    protected void decode(ConversationContext<SocketCANFrame> context, SocketCANFrame msg) throws Exception {
-        CANOpenService service = serviceId(msg.getIdentifier());
-        CANOpenPayload payload = CANOpenPayloadIO.staticParse(new ReadBuffer(msg.getData()), service);
-
-        CANOpenDriverContext.CALLBACK.receive(msg);
+    protected void decode(ConversationContext<CANOpenFrame> context, CANOpenFrame msg) throws Exception {
+        int nodeId = msg.getNodeId();
+        CANOpenService service = msg.getService();
+        CANOpenPayload payload = msg.getPayload();
 
         if (service != null) {
-            int nodeId = Math.abs(service.getMin() - msg.getIdentifier());
-
             if (service.getPdo() && payload instanceof CANOpenPDOPayload) {
                 publishEvent(service, nodeId, (CANOpenPDOPayload) payload);
             } else {
@@ -310,8 +308,6 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
                 }
                 logger.info("Decoded CANOpen {} from {}, message {}, {}", service, nodeId, payload, hex);
             }
-        } else {
-            logger.info("CAN message {}, {}", msg.getIdentifier(), msg);
         }
 
 //        int identifier = msg.getIdentifier();
@@ -385,33 +381,16 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     @Override
-    public void close(ConversationContext<SocketCANFrame> context) {
+    public void close(ConversationContext<CANOpenFrame> context) {
 
     }
 
     @Override
-    public void onDisconnect(ConversationContext<SocketCANFrame> context) {
+    public void onDisconnect(ConversationContext<CANOpenFrame> context) {
         if (this.heartbeat != null) {
             this.heartbeat.cancel();
             this.heartbeat = null;
         }
     }
 
-    private int cobId(int nodeId, CANOpenService service) {
-        // form 32 bit socketcan identifier
-        return service.getMin() + nodeId;
-    }
-
-    private CANOpenService serviceId(int cobId) {
-        // form 32 bit socketcan identifier
-        CANOpenService service = CANOpenService.valueOf((byte) (cobId >> 7));
-        if (service == null) {
-            for (CANOpenService val : CANOpenService.values()) {
-                if (val.getMin() > cobId && val.getMax() < cobId) {
-                    return val;
-                }
-            }
-        }
-        return service;
-    }
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
index c35843d..721b0f1 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANConversation.java
@@ -1,25 +1,25 @@
 package org.apache.plc4x.java.can.socketcan;
 
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
-import org.apache.plc4x.java.can.api.CANFrame;
 import org.apache.plc4x.java.can.api.conversation.canopen.CANConversation;
-import org.apache.plc4x.java.can.api.conversation.canopen.CANFrameBuilder;
-import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.can.canopen.CANOpenFrame;
+import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilder;
+import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilderFactory;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
-import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction;
 
 import java.time.Duration;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
-public class SocketCANConversation implements CANConversation<CANFrame> {
+public class SocketCANConversation implements CANConversation<CANOpenFrame> {
 
     private final int nodeId;
-    private final ConversationContext<SocketCANFrame> context;
+    private final ConversationContext<CANOpenFrame> context;
+    private final CANOpenFrameBuilderFactory factory;
 
-    public SocketCANConversation(int nodeId, ConversationContext<SocketCANFrame> context) {
+    public SocketCANConversation(int nodeId, ConversationContext<CANOpenFrame> context, CANOpenFrameBuilderFactory factory) {
         this.nodeId = nodeId;
         this.context = context;
+        this.factory = factory;
     }
 
     @Override
@@ -28,24 +28,15 @@ public class SocketCANConversation implements CANConversation<CANFrame> {
     }
 
     @Override
-    public CANFrameBuilder<CANFrame> frameBuilder() {
-        return new SocketCANFrameBuilder();
+    public CANOpenFrameBuilder createBuilder() {
+        return factory.createBuilder();
     }
 
     @Override
-    public void send(RequestTransaction transaction, CANFrame frame, BiConsumer<RequestTransaction, SendRequestContext<CANFrame>> callback) {
-        if (frame instanceof SocketCANDelegateFrame) {
-            System.out.println("-----> Sending request frame " + transaction);
-            transaction.submit(() -> {
-                ConversationContext.SendRequestContext<CANFrame> ctx = context.sendRequest(((SocketCANDelegateFrame) frame).getFrame())
-                    .expectResponse(SocketCANFrame.class, Duration.ofSeconds(10L))
-                    .unwrap(SocketCANDelegateFrame::new);
-                System.out.println("-----> Frame been sent " + transaction);
-                callback.accept(transaction, ctx);
-            });
-            return;
-        }
-        throw new PlcRuntimeException("Unsupported frame type " + frame);
+    public void send(CANOpenFrame frame, Consumer<SendRequestContext<CANOpenFrame>> callback) {
+        SendRequestContext<CANOpenFrame> ctx = context.sendRequest(frame)
+            .expectResponse(CANOpenFrame.class, Duration.ofSeconds(10L));
+        callback.accept(ctx);
     }
 
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANDelegateFrame.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANDelegateFrame.java
deleted file mode 100644
index 4bf323e..0000000
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANDelegateFrame.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.apache.plc4x.java.can.socketcan;
-
-import org.apache.plc4x.java.can.api.CANFrame;
-import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
-
-public class SocketCANDelegateFrame implements CANFrame {
-
-    private final SocketCANFrame frame;
-
-    public SocketCANDelegateFrame(SocketCANFrame frame) {
-        this.frame = frame;
-    }
-
-    @Override
-    public int getIdentifier() {
-        return frame.getIdentifier();
-    }
-
-    @Override
-    public boolean getExtended() {
-        return frame.getExtended();
-    }
-
-    @Override
-    public boolean getRemote() {
-        return frame.getRemote();
-    }
-
-    @Override
-    public boolean getError() {
-        return frame.getError();
-    }
-
-    @Override
-    public byte[] getData() {
-        return frame.getData();
-    }
-
-    public SocketCANFrame getFrame() {
-        return frame;
-    }
-
-}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANFrameBuilder.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANFrameBuilder.java
deleted file mode 100644
index b13cd17..0000000
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/socketcan/SocketCANFrameBuilder.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.apache.plc4x.java.can.socketcan;
-
-import org.apache.plc4x.java.can.api.CANFrame;
-import org.apache.plc4x.java.can.api.conversation.canopen.CANFrameBuilder;
-import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
-
-public class SocketCANFrameBuilder implements CANFrameBuilder<CANFrame> {
-
-    private int node;
-    private byte[] data;
-
-    @Override
-    public CANFrameBuilder<CANFrame> node(int node) {
-        this.node = node;
-        return this;
-    }
-
-    @Override
-    public CANFrameBuilder<CANFrame> data(byte[] data) {
-        this.data = data;
-        return this;
-    }
-
-    @Override
-    public CANFrame build() {
-        return new SocketCANDelegateFrame(new SocketCANFrame(node, data));
-    }
-
-}
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenDriverSDOIT.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenDriverSDOIT.java
new file mode 100644
index 0000000..35db6d3
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenDriverSDOIT.java
@@ -0,0 +1,11 @@
+package org.apache.plc4x.java.can;
+
+import org.apache.plc4x.test.driver.DriverTestsuiteRunner;
+
+class CANOpenDriverSDOIT extends DriverTestsuiteRunner {
+
+    public CANOpenDriverSDOIT() {
+        super("/testsuite/CANOpenDriverSDOIT.xml");
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenPDOFieldTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenPDOFieldTest.java
new file mode 100644
index 0000000..432f073
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenPDOFieldTest.java
@@ -0,0 +1,25 @@
+package org.apache.plc4x.java.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CANOpenPDOFieldTest {
+
+    @Test
+    public void testNodeSyntax() {
+        final CANOpenPDOField canField = CANOpenPDOField.of("RECEIVE_PDO_2:20:RECORD");
+
+        assertEquals(20, canField.getNodeId());
+        assertEquals(CANOpenService.RECEIVE_PDO_2, canField.getService());
+        assertEquals(CANOpenDataType.RECORD, canField.getCanOpenDataType());
+    }
+
+    @Test
+    public void testInvalidSyntax() {
+        assertThrows(PlcInvalidFieldException.class, () -> CANOpenPDOField.of("PDO:"));
+    }
+}
\ No newline at end of file


[plc4x] 08/21: Fix socketcan transport.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 8c3122e236febea0321eb30b68b67756c8fece75
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Sat Aug 29 10:08:17 2020 +0200

    Fix socketcan transport.
---
 .../plc4x/java/transport/socketcan/netty/SocketCANChannel.java   | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
index 305d452..b302a7d 100644
--- a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
@@ -114,10 +114,10 @@ public class SocketCANChannel extends OioByteStreamChannel {
             try {
                 while (!isInputShutdown()) {
                     CanFrame frame = handle.read();
-                    ByteBuf frameBytes = ByteBufAllocator.DEFAULT.buffer();
-                    frameBytes.writeBytes(frame.getBuffer());
-                    String dump = ByteBufUtil.prettyHexDump(frameBytes);
-                    System.out.println(frame + "\n" + dump);
+//                    ByteBuf frameBytes = ByteBufAllocator.DEFAULT.buffer();
+//                    frameBytes.writeBytes(frame.getBuffer());
+//                    String dump = ByteBufUtil.prettyHexDump(frameBytes);
+//                    System.out.println(frame + "\n" + dump);
                     buffer.writeBytes(frame.getBuffer());
                 }
             } catch (IOException e) {
@@ -245,6 +245,7 @@ public class SocketCANChannel extends OioByteStreamChannel {
         public void write(byte[] b, int off, int len) throws IOException {
             ByteBuffer buffer = ByteBuffer.allocateDirect(len - off);
             buffer.put(b, off, len);
+            buffer.flip();
             CanFrame frame = CanFrame.create(buffer);
             rawCanChannel.write(frame);
         }


[plc4x] 13/21: Fix socketcan mspec.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 43d130397e58771878cf87348a312d39626daa97
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Thu Sep 3 13:40:38 2020 +0200

    Fix socketcan mspec.
---
 protocols/can/src/main/resources/protocols/can/socketcan.mspec | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/socketcan.mspec b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
index 201c0d6..9440365 100644
--- a/protocols/can/src/main/resources/protocols/can/socketcan.mspec
+++ b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
@@ -18,15 +18,13 @@
  */
 
 [type 'BrokenSocketCANFrame'
-    [discriminator bit 'extended']
     [simple bit 'remote']
     [simple bit 'error']
-    [simple bit 'extended']
+    [discriminator bit 'extended']
     [typeSwitch 'extended'
         ['true' ExtendedSocketCANFrame
             [simple uint 29 'identifier']
         ]
-
         ['false' StandardSocketCANFrame
             [const  uint 18 'spacing' '0x0']
             [simple uint 11 'identifier']
@@ -58,7 +56,7 @@
     [reserved uint 8 '0x0'] // padding 1
     [reserved uint 8 '0x0'] // padding 2
     [array int 8 'data' COUNT 'size']
-    [padding  uint 8 '0x00' '8 - (COUNT(data))']
+    [padding uint 8 'alignment' '0x00' '8 - (COUNT(data))']
 ]
 
 [type 'SocketCAN20AFrame'


[plc4x] 03/21: Step back to virtual fields in can transport.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 16dbb63bc0accd0d11708385e39d8a5bd04a5982
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Mon Aug 24 17:57:02 2020 +0200

    Step back to virtual fields in can transport.
---
 protocols/can/src/main/resources/protocols/can/can.mspec | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/can.mspec b/protocols/can/src/main/resources/protocols/can/can.mspec
index 113e304..16408fd 100644
--- a/protocols/can/src/main/resources/protocols/can/can.mspec
+++ b/protocols/can/src/main/resources/protocols/can/can.mspec
@@ -52,7 +52,7 @@ struct canfd_frame {
 };
 */
 
-[type 'OtherSocketCANFrame'
+[type 'SocketCANFrame'
     [simple int 32 'rawId']
     [virtual bit 'extended'
         'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isRemote", rawId)'
@@ -63,6 +63,7 @@ struct canfd_frame {
     [virtual bit 'error'
         'STATIC_CALL("org.apache.plc4x.java.can.helper.HeaderParser.isError", rawId)'
     ]
+    [implicit uint 8 'size' 'COUNT(data)']
 //    [typeSwitch 'extended'
 //        ['true' ExtendedOtherSocketCanFrame
 //            [simple uint 8 'flags']
@@ -71,13 +72,13 @@ struct canfd_frame {
             [reserved uint 8 '0x0']
 //        ]
 //    ]
-    [reserved uint 8 '0x0']
-    [reserved uint 8 '0x0']
-    [implicit uint 8 'size' 'COUNT(data)']
+    [reserved uint 8 '0x0'] //flags
+    [reserved uint 8 '0x0'] // padding 1
+    [reserved uint 8 '0x0'] // padding 2
     [array int 8 'data' COUNT 'size']
 ]
 
-[type 'SocketCANFrame'
+[type 'SimplifiedSocketCANFrame'
     [simple bit 'extended']
     [simple bit 'remote']
     [simple bit 'error']


[plc4x] 15/21: Test of wireshark frames.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit ce8b123495f6b542eed22d2c06682d32d59b07f6
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Mon Sep 7 13:18:29 2020 +0200

    Test of wireshark frames.
---
 .../src/main/resources/protocols/can/canopen.mspec | 243 +++++++++++++++---
 .../plc4x/java/can/helper/CANOpenHelper.java       |  15 +-
 .../java/can/protocol/CANOpenProtocolLogic.java    |  26 +-
 .../plc4x/java/can/CANOpenWiresharkTest.java       |  30 +++
 .../testsuite/CANOpenPayloadTestSuite.xml          | 130 +++++-----
 .../testsuite/CANOpenWiresharkTestSuite.xml        | 278 +++++++++++++++++++++
 6 files changed, 616 insertions(+), 106 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index bb825f3..e5e0fe1 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -36,7 +36,7 @@
 ]
 
 [enum uint 8 'NMTStateRequest'
-    ['0x01' OPERATIONAL]
+    ['0x01' START]
     ['0x02' STOP]
     ['0x80' PRE_OPERATIONAL]
     ['0x81' RESET_NODE]
@@ -58,7 +58,7 @@
             [simple uint 7 'node']
         ]
         ['CANOpenService.TIME' CANOpenTimeSynchronization
-            [simple TimeOfDay 'timeOfDay']
+            [simple CANOpenTime 'timeOfDay']
         ]
         ['CANOpenService.RECEIVE_PDO_1' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['1', 'true']]
@@ -84,12 +84,12 @@
         ['CANOpenService.TRANSMIT_PDO_4' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['1', 'false']]
         ]
-        ['CANOpenService.TRANSMIT_SDO' CANOpenSDORequest
-            [enum SDOCommand 'command']
+        ['CANOpenService.RECEIVE_SDO' CANOpenSDORequest
+            [enum SDORequestCommand 'command']
             [simple SDORequest 'request' ['command']]
         ]
-        ['CANOpenService.RECEIVE_SDO' CANOpenSDOResponse
-            [enum SDOCommand 'command']
+        ['CANOpenService.TRANSMIT_SDO' CANOpenSDOResponse
+            [enum SDOResponseCommand 'command']
             [simple SDOResponse 'response' ['command']]
         ]
         ['CANOpenService.HEARTBEAT' CANOpenHeartbeatPayload
@@ -98,82 +98,243 @@
     ]
 ]
 
-[type 'SDORequest' [SDOCommand 'command']
+[type 'SDORequest' [SDORequestCommand 'command']
     [typeSwitch 'command'
-        ['SDOCommand.INITIALIZE_DOWNLOAD' SDOInitializeDownloadRequest
+        ['SDORequestCommand.SEGMENT_DOWNLOAD' SDOSegmentDownloadRequest
+            [simple bit 'toggle']
+            [implicit uint 3 'size' '7 - COUNT(data)']
+            [simple bit 'last']
+            [array int 8 'data' COUNT '7 - size']
+            [padding uint 8 'alignment' '0x00' '7 - COUNT(data)']
+        ]
+        ['SDORequestCommand.INITIATE_DOWNLOAD' SDOInitiateDownloadRequest
             [reserved uint 1 '0x00']
-            [implicit uint 2 'size' 'expedited && indicated ? 4 - COUNT(data) : 0']
+            [implicit uint 2 'size' 'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.count", expedited, indicated, payload)']
             [simple bit 'expedited']
             [simple bit 'indicated']
-            [simple Multiplexer 'address']
-            [array int 8 'data' COUNT '(expedited && indicated) ? 4 - size : 0']
-            [padding uint 8 'alignment' '0x00' '4 - (COUNT(data))']
+            [simple IndexAddress 'address']
+            [simple SDOInitiateUploadResponsePayload 'payload' ['expedited', 'indicated', 'size']]
+        ]
+        ['SDORequestCommand.INITIATE_UPLOAD' SDOInitiateUploadRequest
+            [reserved uint 5 '0x00']
+            [simple IndexAddress 'address']
+            [reserved int 32 '0x00'] // padding
+        ]
+        ['SDORequestCommand.SEGMENT_UPLOAD' SDOSegmentUploadRequest
+            [simple bit 'toggle']
+            [reserved uint 4 '0x00']
+            [reserved int 56 '0x00'] // padding
+        ]
+        ['SDORequestCommand.ABORT' SDOAbortRequest
+            [simple SDOAbort 'abort']
+        ]
+        ['SDORequestCommand.BLOCK' SDOBlockRequest
+            [simple SDOBlockData 'block']
         ]
-        ['SDOCommand.SEGMENT_DOWNLOAD' SDOSegmentDownloadRequest
+    ]
+]
+
+[type 'SDOBlockData'
+    [simple uint 5 'flags']
+    [array int 8 'data' COUNT '7']
+]
+
+[type 'SDOResponse' [SDOResponseCommand 'command']
+    [typeSwitch 'command'
+        ['SDOResponseCommand.SEGMENT_UPLOAD' SDOSegmentUploadResponse
             [simple bit 'toggle']
             [implicit uint 3 'size' '7 - COUNT(data)']
             [simple bit 'last']
-            [array int 8 'data' COUNT '7 - data']
-            [padding uint 8 'alignment' '0x00' '7 - (COUNT(data))']
+            [array int 8 'data' COUNT '7 - size']
+            [padding uint 8 'alignment' '0x00' '7 - COUNT(data)']
+        ]
+        ['SDOResponseCommand.SEGMENT_DOWNLOAD' SDOSegmentDownloadResponse
+            [simple bit 'toggle']
+            [reserved uint 4 '0x00'] // fill first byte
+            [reserved int 56 '0x00'] // padding
+        ]
+        ['SDOResponseCommand.INITIATE_UPLOAD' SDOInitiateUploadResponse
+            [reserved uint 1 '0x00']
+            [implicit uint 2 'size' 'STATIC_CALL("org.apache.plc4x.java.can.helper.CANOpenHelper.count", expedited, indicated, payload)']
+            [simple bit 'expedited']
+            [simple bit 'indicated']
+            [simple IndexAddress 'address']
+            [simple SDOInitiateUploadResponsePayload 'payload' ['expedited', 'indicated', 'size']]
         ]
-        ['SDOCommand.INITIALIZE_UPLOAD' SDOInitializeUploadRequest
+        ['SDOResponseCommand.INITIATE_DOWNLOAD' SDOInitiateDownloadResponse
             [reserved uint 5 '0x00']
-            [simple Multiplexer 'address']
+            [simple IndexAddress 'address']
             [reserved int 32 '0x00'] // padding
         ]
+        ['SDOResponseCommand.ABORT' SDOAbortResponse
+            [simple SDOAbort 'abort']
+        ]
+        ['SDOResponseCommand.BLOCK' SDOBlockResponse
+            [simple SDOBlockData 'block']
+        ]
     ]
 ]
 
-[type 'SDOResponse' [SDOCommand 'command']
-    [typeSwitch 'command'
-        ['SDOCommand.SEGMENT_UPLOAD' SDOSegmentUploadResponse
-            [reserved uint 5 '0x00']
-            [simple Multiplexer 'address']
-            [reserved int 32 '0x00'] // padding
+[type 'SDOInitiateUploadResponsePayload' [bit 'expedited', bit 'indicated', uint 2 'size']
+    [typeSwitch 'expedited', 'indicated'
+        ['true', 'true' SDOInitiateExpeditedUploadResponse [uint 2 'size']
+            [array int 8 'data' COUNT '4 - size']
+            [padding uint 8 'alignment' '0x00' '4 - COUNT(data)']
         ]
-        ['SDOCommand.INITIALIZE_DOWNLOAD' SDOInitializeDownloadResponse
-            [simple bit 'toggle']
-            [reserved uint 4 '0x00']
-            [reserved int 32 '0x00'] // padding
+        ['false', 'true' SDOInitiateSegmentedUploadResponse
+            [simple uint 32 'bytes']
         ]
-        ['SDOCommand.INITIALIZE_UPLOAD' SDOInitializeUploadResponse
-            [simple SDOSegment 'segment']
+        ['false', 'false' SDOInitiateSegmentedReservedResponse
+            [reserved int 32 '0x00']
         ]
     ]
 ]
 
+[type 'SDOAbort'
+    [reserved uint 5 '0x00']
+    [simple IndexAddress 'address']
+    [simple uint 32 'code']
+]
+
 [type 'SDOSegment'
     [reserved uint 1 '0x00']
     [implicit uint 2 'size' 'expedited && indicated ? 4 - COUNT(data) : 0']
     [simple bit 'expedited']
     [simple bit 'indicated']
-    [simple Multiplexer 'address']
+    [simple IndexAddress 'address']
     [array int 8 'data' COUNT '(expedited && indicated) ? 4 - size : 0']
     [padding uint 8 'alignment' '0x00' '4 - (COUNT(data))']
 ]
 
-[type 'Multiplexer'
+[type 'IndexAddress'
     [simple uint 16 'index']
     [simple uint 8 'subindex']
 ]
 
-[enum uint 3 'SDOCommand'
-    ['0x00' SEGMENT_DOWNLOAD]
-    ['0x01' INITIALIZE_DOWNLOAD]
-    ['0x02' INITIALIZE_UPLOAD]
-    ['0x03' SEGMENT_UPLOAD]
-    ['0x04' ABORT]
-    ['0x05' BLOCK_UPLOAD]
-    ['0x06' BLOCK_DOWNLOAD]
+[enum uint 3 'SDORequestCommand'
+    ['0x00' SEGMENT_DOWNLOAD  ]
+    ['0x01' INITIATE_DOWNLOAD ]
+    ['0x02' INITIATE_UPLOAD   ]
+    ['0x03' SEGMENT_UPLOAD    ]
+    ['0x04' ABORT             ]
+    ['0x05' BLOCK             ]
+]
+
+[enum uint 3 'SDOResponseCommand'
+    ['0x00' SEGMENT_UPLOAD    ]
+    ['0x01' SEGMENT_DOWNLOAD  ]
+    ['0x02' INITIATE_UPLOAD   ]
+    ['0x03' INITIATE_DOWNLOAD ]
+    ['0x04' ABORT             ]
+    ['0x06' BLOCK             ]
 ]
 
 [type 'CANOpenPDO' [uint 2 'index', bit 'receive']
     [array int 8 'data' COUNT '8']
 ]
 
-[type 'TimeOfDay'
-    // CiA 301 - section 7.1.6.5
+[type 'CANOpenTime'
+    // CiA 301 - section 7.1.6.5 and 7.1.6.6
     [simple uint 28 'millis']
     [reserved int 4 '0x00']
     [simple uint 16 'days']
+]
+
+[enum 'CANOpenDataType' [uint 8 'numBits']
+    [BOOLEAN     [ '1'] ]
+    [UNSIGNED8   [ '8'] ]
+    [UNSIGNED16  ['16'] ]
+    [UNSIGNED24  ['24'] ]
+    [UNSIGNED32  ['32'] ]
+    [UNSIGNED40  ['40'] ]
+    [UNSIGNED48  ['48'] ]
+    [UNSIGNED56  ['56'] ]
+    [UNSIGNED64  ['64'] ]
+    [INTEGER8    [ '8'] ]
+    [INTEGER16   ['16'] ]
+    [INTEGER24   ['24'] ]
+    [INTEGER32   ['32'] ]
+    [INTEGER40   ['40'] ]
+    [INTEGER48   ['48'] ]
+    [INTEGER56   ['56'] ]
+    [INTEGER64   ['64'] ]
+    [REAL32      ['32'] ]
+    [REAL64      ['64'] ]
+
+    // compound/complex types
+    [OCTET_STRING     [ '8'] ]
+    [VISIBLE_STRING   [ '8'] ]
+    [UNICODE_STRING   ['16'] ]
+    [TIME_OF_DAY      ['48'] ]
+    [TIME_DIFFERENCE  ['48'] ]
+]
+
+[dataIo 'DataItem' [CANOpenDataType 'dataType']
+    [typeSwitch 'dataType'
+        ['CANOpenDataType.BOOLEAN' Boolean
+            [simple bit 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED8' Integer
+            [simple uint 8 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED16' Integer
+            [simple uint 16 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED24' Long
+            [simple uint 24 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED32' Long
+            [simple uint 32 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED40' BigInteger
+            [simple uint 40 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED48' BigInteger
+            [simple uint 48 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED56' BigInteger
+            [simple uint 56 'value']
+        ]
+        ['CANOpenDataType.UNSIGNED64' BigInteger
+            [simple uint 64 'value']
+        ]
+        ['CANOpenDataType.INTEGER8' Integer
+            [simple int 8 'value']
+        ]
+        ['CANOpenDataType.INTEGER16' Integer
+            [simple int 16 'value']
+        ]
+        ['CANOpenDataType.INTEGER24' Integer
+            [simple int 24 'value']
+        ]
+        ['CANOpenDataType.INTEGER32' Integer
+            [simple int 32 'value']
+        ]
+        ['CANOpenDataType.INTEGER40' Long
+            [simple int 40 'value']
+        ]
+        ['CANOpenDataType.INTEGER48' Long
+            [simple int 48 'value']
+        ]
+        ['CANOpenDataType.INTEGER56' Long
+            [simple int 56 'value']
+        ]
+        ['CANOpenDataType.INTEGER64' Long
+            [simple int 64 'value']
+        ]
+        ['CANOpenDataType.REAL32' Float
+            [simple float 8.23 'value']
+        ]
+        ['CANOpenDataType.REAL64' Double
+            [simple float 11.52 'value']
+        ]
+        ['CANOpenDataType.OCTET_STRING' String
+        ]
+        ['CANOpenDataType.VISIBLE_STRING' String
+        ]
+        //CANOpenDataType.TIME_OF_DAY' CANOpenTime
+        //CANOpenDataType.TIME_DIFFERENCE' CANOpenTime
+        ['CANOpenDataType.UNICODE_STRING' String
+        ]
+    ]
 ]
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
index 5f6b4d3..1a9a346 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/helper/CANOpenHelper.java
@@ -1,16 +1,27 @@
 package org.apache.plc4x.java.can.helper;
 
+import org.apache.plc4x.java.canopen.readwrite.SDOInitiateExpeditedUploadResponse;
+import org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadResponsePayload;
+import org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadResponse;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
-import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
-import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
+import static org.apache.plc4x.java.spi.generation.StaticHelper.COUNT;
+
 public class CANOpenHelper {
 
     public static CANOpenService readFunction(short identifier) {
         return CANOpenService.valueOf((byte) (identifier >> 7));
     }
 
+    public static int uploadPadding(SDOSegmentUploadResponse payload) {
+        return 7 - payload.getData().length;
+    }
+
+    public static int count(boolean expedited, boolean indicated, SDOInitiateUploadResponsePayload payload) {
+        return expedited && indicated && payload instanceof SDOInitiateExpeditedUploadResponse ? 4 - COUNT(((SDOInitiateExpeditedUploadResponse) payload).getData()) : 0;
+    }
+
     public static void writeFunction(WriteBuffer io, short identifier) {
         // NOOP - a placeholder to let mspec compile
     }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index c193ec3..fa6dab2 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -20,10 +20,8 @@ package org.apache.plc4x.java.can.protocol;
 
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload;
-import org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenHeartbeatPayloadIO;
-import org.apache.plc4x.java.canopen.readwrite.io.CANOpenNetworkPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.canopen.readwrite.types.NMTState;
@@ -88,7 +86,14 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
     @Override
     protected void decode(ConversationContext<SocketCANFrame> context, SocketCANFrame msg) throws Exception {
-        logger.info("Decode CAN message {}", msg);
+        CANOpenService service = serviceId(msg.getIdentifier());
+        CANOpenPayload payload = CANOpenPayloadIO.staticParse(new ReadBuffer(msg.getData()), service);
+
+        if (service != null) {
+            logger.info("Decoded CANOpen {} from {}, message {}", service, Math.abs(service.getMin() - msg.getIdentifier()), payload);
+        } else {
+            logger.info("CAN message {}, {}", msg.getIdentifier(), msg);
+        }
 
 //        int identifier = msg.getIdentifier();
 //        CANOpenService service = CANOpenService.valueOf((byte) (identifier >> 7));
@@ -107,7 +112,10 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
     @Override
     public void onDisconnect(ConversationContext<SocketCANFrame> context) {
-
+        if (this.heartbeat != null) {
+            this.heartbeat.cancel();
+            this.heartbeat = null;
+        }
     }
 
     private int cobId(CANOpenService service) {
@@ -118,6 +126,14 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
     private CANOpenService serviceId(int nodeId) {
         // form 32 bit socketcan identifier
-        return CANOpenService.valueOf((byte) (nodeId >> 7));
+        CANOpenService service = CANOpenService.valueOf((byte) (nodeId >> 7));
+        if (service == null) {
+            for (CANOpenService val : CANOpenService.values()) {
+                if (val.getMin() > nodeId && val.getMax() < nodeId) {
+                    return val;
+                }
+            }
+        }
+        return service;
     }
 }
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenWiresharkTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenWiresharkTest.java
new file mode 100644
index 0000000..3d1258a
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenWiresharkTest.java
@@ -0,0 +1,30 @@
+/*
+  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.can;
+
+import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
+
+public class CANOpenWiresharkTest extends ParserSerializerTestsuiteRunner {
+
+    public CANOpenWiresharkTest() {
+        super("/testsuite/CANOpenWiresharkTestSuite.xml");
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
index 8537aee..2f2353f 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
@@ -34,7 +34,7 @@
     </parser-arguments>
     <xml>
       <CANOpenTimeSynchronization className="org.apache.plc4x.java.canopen.readwrite.CANOpenTimeSynchronization">
-        <timeOfDay className="org.apache.plc4x.java.canopen.readwrite.TimeOfDay">
+        <timeOfDay className="org.apache.plc4x.java.canopen.readwrite.CANOpenTime">
           <millis>-663027446</millis>
           <days>13392</days>
         </timeOfDay>
@@ -54,7 +54,7 @@
     </parser-arguments>
     <xml>
       <CANOpenTimeSynchronization className="org.apache.plc4x.java.canopen.readwrite.CANOpenTimeSynchronization">
-        <timeOfDay className="org.apache.plc4x.java.canopen.readwrite.TimeOfDay">
+        <timeOfDay className="org.apache.plc4x.java.canopen.readwrite.CANOpenTime">
           <millis>-392494841</millis>
           <days>13392</days>
         </timeOfDay>
@@ -105,27 +105,33 @@
   </testcase>
 
   <testcase>
-    <name>SDO request, payload: 43171000</name>
-    <raw>43171000</raw>
+    <name>SDO request, payload: 4317100000000000</name>
+    <raw>4317100000000000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>RECEIVE_SDO</arg1>
+      <arg1>TRANSMIT_SDO</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
-        <command>INITIALIZE_UPLOAD</command>
-        <expedited>true</expedited>
-        <placement>true</placement>
-        <index>4119</index>
-        <subindex>0</subindex>
-        <data/>
-      </CANOpenSDORequest>
+      <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+        <command>INITIATE_UPLOAD</command>
+        <response className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadResponse">
+          <expedited>true</expedited>
+          <indicated>true</indicated>
+          <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+            <index>4119</index>
+            <subindex>0</subindex>
+          </address>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateExpeditedUploadResponse">
+            <data>AAAAAA==</data>
+          </payload>
+        </response>
+      </CANOpenSDOResponse>
     </xml>
   </testcase>
 
   <testcase>
-    <name>SDO request, payload: 00171000<!--A00F0000--></name>
-    <raw>00171000<!--A00F0000--></raw>
+    <name>SDO request, payload: 00171000A00F0000</name>
+    <raw>00171000A00F0000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
       <arg1>RECEIVE_SDO</arg1>
@@ -133,87 +139,93 @@
     <xml>
       <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
         <command>SEGMENT_DOWNLOAD</command>
-        <expedited>false</expedited>
-        <placement>false</placement>
-        <index>4119</index>
-        <subindex>0</subindex>
-        <data/>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentDownloadRequest">
+          <toggle>false</toggle>
+          <last>false</last>
+          <data>FxAAoA8AAA==</data>
+        </request>
       </CANOpenSDORequest>
     </xml>
   </testcase>
 
   <testcase>
-    <name>SDO write request, payload: 2B171000D00F</name>
-    <raw>2B171000D00F</raw>
+    <name>SDO write request, payload: 2B171000D00F0000</name>
+    <raw>2B171000D00F0000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
       <arg1>RECEIVE_SDO</arg1>
     </parser-arguments>
     <xml>
       <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
-        <command>INITIALIZE_DOWNLOAD</command>
-        <expedited>true</expedited>
-        <placement>true</placement>
-        <index>4119</index>
-        <subindex>0</subindex>
-        <data>
-          <data>208</data>
-          <data>15</data>
-        </data>
+        <command>INITIATE_DOWNLOAD</command>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateDownloadRequest">
+          <expedited>true</expedited>
+          <indicated>true</indicated>
+          <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+            <index>4119</index>
+            <subindex>0</subindex>
+          </address>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateExpeditedUploadResponse">
+            <data>0A8=</data>
+          </payload>
+        </request>
       </CANOpenSDORequest>
     </xml>
   </testcase>
 
   <testcase>
-    <name>SDO write result, payload: 60171000<!--A00F0000--></name>
-    <raw>00171000<!--A00F0000--></raw>
+    <name>SDO write result, payload: 60171000A00F0000</name>
+    <raw>00171000A00F0000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
       <arg1>TRANSMIT_SDO</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
-        <command>SEGMENT_DOWNLOAD</command>
-        <expedited>false</expedited>
-        <placement>false</placement>
-        <index>4119</index>
-        <subindex>0</subindex>
-        <data/>
-      </CANOpenSDORequest>
+      <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+        <command>SEGMENT_UPLOAD</command>
+        <response className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadResponse">
+          <toggle>false</toggle>
+          <last>false</last>
+          <data>FxAAoA8AAA==</data>
+        </response>
+      </CANOpenSDOResponse>
     </xml>
   </testcase>
 
   <!-- samples -->
   <testcase>
-    <name>SDO response, payload: 60002000</name>
-    <raw>60002000</raw>
+    <name>SDO response, payload: 6000200000000000</name>
+    <raw>6000200000000000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
       <arg1>TRANSMIT_SDO</arg1>
     </parser-arguments>
     <xml>
       <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
-        <command>SEGMENT_UPLOAD</command>
-        <expedited>false</expedited>
-        <placement>false</placement>
-        <index>8192</index>
-        <subindex>0</subindex>
-        <data/>
+        <command>INITIATE_DOWNLOAD</command>
+        <response className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateDownloadResponse">
+          <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+            <index>8192</index>
+            <subindex>0</subindex>
+          </address>
+        </response>
       </CANOpenSDOResponse>
     </xml>
   </testcase>
 
   <testcase>
-    <name>PDO, payload: 78563412</name>
-    <raw>78563412</raw>
+    <name>PDO, payload: 7856341200000000</name>
+    <raw>7856341200000000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>RECEIVE_SDO</arg1>
+      <arg1>RECEIVE_PDO_1</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
-        <state>PRE_OPERATIONAL</state>
-      </CANOpenNetworkPayload>
+      <CANOpenPDOPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenPDOPayload">
+        <pdo className="org.apache.plc4x.java.canopen.readwrite.CANOpenPDO">
+          <data>eFY0EgAAAAA=</data>
+        </pdo>
+      </CANOpenPDOPayload>
     </xml>
   </testcase>
 
@@ -226,9 +238,11 @@
       <arg1>TRANSMIT_PDO_1</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
-        <state>PRE_OPERATIONAL</state>
-      </CANOpenNetworkPayload>
+      <CANOpenPDOPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenPDOPayload">
+        <pdo className="org.apache.plc4x.java.canopen.readwrite.CANOpenPDO">
+          <data>+QE+AUwBXAE=</data>
+        </pdo>
+      </CANOpenPDOPayload>
     </xml>
   </testcase>
 
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenWiresharkTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenWiresharkTestSuite.xml
new file mode 100644
index 0000000..4bced77
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenWiresharkTestSuite.xml
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<test:testsuite xmlns:test="https://plc4x.apache.org/schemas/parser-serializer-testsuite.xsd">
+
+  <name>Tests of CANopen frames from Wireshark sample PCAP files.</name>
+
+
+  <testcase>
+    <name>SDO initiate request: 4008100000000000</name>
+    <raw>4008100000000000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>RECEIVE_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>INITIATE_UPLOAD</command>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadRequest">
+          <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+            <index>4104</index>
+            <subindex>0</subindex>
+          </address>
+        </request>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO initiate response: 4108100015000000</name>
+    <raw>4108100015000000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>TRANSMIT_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+        <command>INITIATE_UPLOAD</command>
+        <response className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadResponse">
+          <expedited>false</expedited>
+          <indicated>true</indicated>
+          <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+            <index>4104</index>
+            <subindex>0</subindex>
+          </address>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateSegmentedUploadResponse">
+            <bytes>21</bytes>
+          </payload>
+        </response>
+      </CANOpenSDOResponse>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO segment upload request: 6000000000000000</name>
+    <raw>6000000000000000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>RECEIVE_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>SEGMENT_UPLOAD</command>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadRequest">
+          <toggle>false</toggle>
+        </request>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO abort: 8000000021000008</name>
+    <raw>8000000021000008</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>RECEIVE_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>ABORT</command>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOAbortRequest">
+          <abort className="org.apache.plc4x.java.canopen.readwrite.SDOAbort">
+            <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+              <index>0</index>
+              <subindex>0</subindex>
+            </address>
+            <code>134217761</code>
+          </abort>
+        </request>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO upload segment response: 00556c7472614869</name>
+    <raw>00556c7472614869</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>TRANSMIT_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+        <command>SEGMENT_UPLOAD</command>
+        <response className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadResponse">
+          <toggle>false</toggle>
+          <last>false</last>
+          <data>VWx0cmFIaQ==</data>
+        </response>
+      </CANOpenSDOResponse>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO upload segment request: 7000000000000000</name>
+    <raw>7000000000000000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>RECEIVE_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>SEGMENT_UPLOAD</command>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadRequest">
+          <toggle>true</toggle>
+        </request>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO initiate upload request: 4008102a00000000</name>
+    <raw>4008102a00000000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>RECEIVE_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>INITIATE_UPLOAD</command>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadRequest">
+          <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+            <index>4104</index>
+            <subindex>42</subindex>
+          </address>
+        </request>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>NMT start node 0x42: 0142</name>
+    <raw>0142</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>NMT</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <request>START</request>
+        <node>66</node>
+      </CANOpenNetworkPayload>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>NMT start node 0x00 (all): 0100</name>
+    <raw>0100</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>NMT</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <request>START</request>
+        <node>0</node>
+      </CANOpenNetworkPayload>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>NMT start node 0x42: 0263</name>
+    <raw>0263</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>NMT</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <request>STOP</request>
+        <node>99</node>
+      </CANOpenNetworkPayload>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO initiate download request: 2300620161736466</name>
+    <raw>2300620161736466</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>RECEIVE_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>INITIATE_DOWNLOAD</command>
+        <request className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateDownloadRequest">
+          <expedited>true</expedited>
+          <indicated>true</indicated>
+          <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+            <index>25088</index>
+            <subindex>1</subindex>
+          </address>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateExpeditedUploadResponse">
+            <data>YXNkZg==</data>
+          </payload>
+        </request>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO abort: 8000620100000206</name>
+    <raw>8000620100000206</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>TRANSMIT_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+        <command>ABORT</command>
+        <response className="org.apache.plc4x.java.canopen.readwrite.SDOAbortResponse">
+          <abort className="org.apache.plc4x.java.canopen.readwrite.SDOAbort">
+            <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+              <index>25088</index>
+              <subindex>1</subindex>
+            </address>
+            <code>100794368</code>
+          </abort>
+        </response>
+      </CANOpenSDOResponse>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO block upload: c208100015000000</name>
+    <raw>c208100015000000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>TRANSMIT_SDO</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+        <command>BLOCK</command>
+        <response className="org.apache.plc4x.java.canopen.readwrite.SDOBlockResponse">
+          <block className="org.apache.plc4x.java.canopen.readwrite.SDOBlockData">
+            <flags>2</flags>
+            <data>CBAAFQAAAA==</data>
+          </block>
+        </response>
+      </CANOpenSDOResponse>
+    </xml>
+  </testcase>
+</test:testsuite>
\ No newline at end of file


[plc4x] 09/21: Sketch of support for SDO operations.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit d5ff622042011eb3392a2a1eab34518f925f130b
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Sat Aug 29 10:09:57 2020 +0200

    Sketch of support for SDO operations.
---
 .../src/main/resources/protocols/can/canopen.mspec |  40 ++++++-
 .../apache/plc4x/java/can/CANOpenPlcDriver.java    |  17 +--
 .../java/can/configuration/CANConfiguration.java   |  12 +++
 .../java/can/protocol/CANOpenProtocolLogic.java    |  58 ++++++-----
 .../test/java/org/apache/plc4x/java/can/Main.java  |   2 +-
 .../testsuite/CANOpenPayloadTestSuite.xml          | 115 ++++++++++++++++++++-
 6 files changed, 196 insertions(+), 48 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index 54337b4..d5160c6 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -18,8 +18,10 @@
  */
 
 [enum uint 4 'CANOpenService'
-    ['0x00' BROADCAST]
-    ['0x07' NMT]
+    ['0b0000' BROADCAST   ]
+    ['0b1110' NMT         ]
+    ['0b1100' SDO_REQUEST ]
+    ['0b1011' SDO_RESPONSE]
 ]
 
 [enum uint 8 'NMTStateRequest'
@@ -41,11 +43,41 @@
     [typeSwitch 'function'
         ['CANOpenService.BROADCAST' CANOpenBroadcastPayload
             [enum NMTStateRequest 'request']
-            [reserved uint 1 '0x0']
+            [reserved uint 1 '0x00']
             [simple uint 7 'node']
         ]
         ['CANOpenService.NMT' CANOpenNetworkPayload
             [enum NMTState 'state']
         ]
+        ['CANOpenService.SDO_REQUEST' CANOpenSDORequest
+            [enum SDOCommand 'command']
+            [reserved uint 1 '0x00']
+            [implicit uint 2 'size' 'COUNT(data)']
+            [simple bit 'expedited'] // segmented
+            [simple bit 'placement']
+            [simple uint 16 'index']
+            [simple uint 8 'subindex']
+            [array uint 8 'data' COUNT 'size']
+        ]
+        ['CANOpenService.SDO_RESPONSE' CANOpenSDOResponse
+            [enum SDOCommand 'command']
+            [reserved uint 1 '0x00']
+            [implicit uint 2 'size' 'COUNT(data)']
+            [simple bit 'expedited'] // segmented
+            [simple bit 'placement']
+            [simple uint 16 'index']
+            [simple uint 8 'subindex']
+            [array uint 8 'data' COUNT 'size']
+        ]
     ]
-]
\ No newline at end of file
+]
+
+[enum uint 3 'SDOCommand'
+    ['0x00' SEGMENT_DOWNLOAD]
+    ['0x01' INITIALIZE_DOWNLOAD]
+    ['0x02' INITIALIZE_UPLOAD]
+    ['0x03' SEGMENT_UPLOAD]
+    ['0x04' ABORT]
+    ['0x05' BLOCK_UPLOAD]
+    ['0x06' BLOCK_DOWNLOAD]
+]
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
index 4255ff2..6acff67 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
@@ -78,25 +78,10 @@ public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
         @Override
         public int applyAsInt(ByteBuf byteBuf) {
             if (byteBuf.readableBytes() >= 5) {
-
-                System.out.println(ByteBufUtil.prettyHexDump(byteBuf));
-                byte len = byteBuf.getByte(4);
-                System.out.println("Length " + (int) len);
-
-                CanFrame frame = CanFrame.create(byteBuf.nioBuffer());
-                System.out.println(frame);
-
-                return len + 8 /* overhead */;
+                return 16; // socketcan transport always returns 16 bytes padded with zeros;
             }
             return -1; //discard
         }
     }
 
-    public class CANCleaner implements Consumer<ByteBuf> {
-        @Override
-        public void accept(ByteBuf byteBuf) {
-            System.out.println("Discard");
-            byteBuf.readByte();
-        }
-    }
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
index 925ca15..214794d 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
@@ -27,6 +27,9 @@ public class CANConfiguration implements Configuration, CANTransportConfiguratio
     @ConfigurationParameter
     private int nodeId;
 
+    @ConfigurationParameter
+    private boolean hearbeat;
+
     public int getNodeId() {
         return nodeId;
     }
@@ -34,4 +37,13 @@ public class CANConfiguration implements Configuration, CANTransportConfiguratio
     public void setNodeId(int nodeId) {
         this.nodeId = nodeId;
     }
+
+    public boolean isHeartbeat() {
+        return hearbeat;
+    }
+
+    public void setHearbeat(boolean hearbeat) {
+        this.hearbeat = hearbeat;
+    }
+
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index 555ef92..9cc2fdb 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -45,6 +45,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
     private CANConfiguration configuration;
     private RequestTransactionManager tm;
+    private Timer heartbeat;
 
     @Override
     public void setConfiguration(CANConfiguration configuration) {
@@ -55,39 +56,46 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
 
     @Override
     public void onConnect(ConversationContext<SocketCANFrame> context) {
-        CANOpenNetworkPayload state = new CANOpenNetworkPayload(NMTState.BOOTED_UP);
-        WriteBuffer buffer = new WriteBuffer(1);
         try {
-            CANOpenNetworkPayloadIO.staticSerialize(buffer, state);
-            context.sendToWire(new SocketCANFrame(cobId(CANOpenService.NMT), buffer.getData()));
-            context.fireConnected();
-
-            Timer heartbeat = new Timer();
-            heartbeat.scheduleAtFixedRate(new TimerTask() {
-                @Override
-                public void run() {
-                    CANOpenNetworkPayload state = new CANOpenNetworkPayload(NMTState.OPERATIONAL);
-                    WriteBuffer buffer = new WriteBuffer(1);
-                    context.sendToWire(new SocketCANFrame(cobId(CANOpenService.NMT), buffer.getData()));
-                }
-            }, 5000, 5000);
+            if (configuration.isHeartbeat()) {
+                context.sendToWire(createFrame(new CANOpenNetworkPayload(NMTState.BOOTED_UP)));
+                context.fireConnected();
+
+                this.heartbeat = new Timer();
+                this.heartbeat.scheduleAtFixedRate(new TimerTask() {
+                    @Override
+                    public void run() {
+                        try {
+                            context.sendToWire(createFrame(new CANOpenNetworkPayload(NMTState.OPERATIONAL)));
+                        } catch (ParseException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }, 5000, 5000);
+            }
         } catch (ParseException e) {
             e.printStackTrace();
         }
     }
 
+    private SocketCANFrame createFrame(CANOpenNetworkPayload state) throws ParseException {
+        WriteBuffer buffer = new WriteBuffer(state.getLengthInBytes());
+        CANOpenNetworkPayloadIO.staticSerialize(buffer, state);
+        return new SocketCANFrame(cobId(CANOpenService.NMT), buffer.getData());
+    }
+
     @Override
     protected void decode(ConversationContext<SocketCANFrame> context, SocketCANFrame msg) throws Exception {
         logger.info("Decode CAN message {}", msg);
 
-        int identifier = msg.getIdentifier();
-        CANOpenService service = CANOpenService.valueOf((byte) (identifier >> 7));
-        if (service != null) {
-            ReadBuffer buffer = new ReadBuffer(msg.getData());
-            CANOpenPayload payload = CANOpenPayloadIO.staticParse(buffer, service);
-
-
-        }
+//        int identifier = msg.getIdentifier();
+//        CANOpenService service = CANOpenService.valueOf((byte) (identifier >> 7));
+//        if (service != null) {
+//            ReadBuffer buffer = new ReadBuffer(msg.getData());
+//            CANOpenPayload payload = CANOpenPayloadIO.staticParse(buffer, service);
+//
+//
+//        }
     }
 
     @Override
@@ -101,7 +109,9 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     }
 
     private int cobId(CANOpenService service) {
-        return service.getValue() & configuration.getNodeId();
+        // form 32 bit socketcan identifier
+        return (configuration.getNodeId() << 24) & 0xff000000 |
+            (service.getValue() << 16 ) & 0x00ff0000;
     }
 
 }
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
index e8279b6..8212bea 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
@@ -29,7 +29,7 @@ public class Main {
     public static void main(String[] args) throws Exception {
         PlcDriverManager driverManager = new PlcDriverManager();
 
-        PlcConnection connection = driverManager.getConnection("canopen:javacan://vcan0?nodeId=15");
+        PlcConnection connection = driverManager.getConnection("canopen:javacan://vcan0?nodeId=11");
 
     }
 
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
index 773f608..d7c1a33 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
@@ -19,10 +19,10 @@
   -->
 <test:testsuite xmlns:test="https://plc4x.apache.org/schemas/parser-serializer-testsuite.xsd">
 
-  <name>Tests of socketcan/CANopen frames.</name>
+  <name>Tests of CANopen frames payload.</name>
 
   <testcase>
-    <name>Network heartbeat payload: 00</name>
+    <name>Network heartbeat, payload: 00</name>
     <raw>00</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
@@ -36,7 +36,7 @@
   </testcase>
 
   <testcase>
-    <name>Network heartbeat payload: 7F</name>
+    <name>Network heartbeat, payload: 7F</name>
     <raw>7F</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
@@ -49,4 +49,113 @@
     </xml>
   </testcase>
 
+  <testcase>
+    <name>SDO request, payload: 4317100000000000</name>
+    <raw>4317100000000000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>SDO_REQUEST</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>INITIALIZE_UPLOAD</command>
+        <expedited>true</expedited>
+        <placement>true</placement>
+        <index>4119</index>
+        <subindex>0</subindex>
+        <data/>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO request, payload: 00171000A00F0000</name>
+    <raw>00171000A00F0000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>SDO_REQUEST</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>SEGMENT_DOWNLOAD</command>
+        <expedited>false</expedited>
+        <placement>false</placement>
+        <index>4119</index>
+        <subindex>0</subindex>
+        <data/>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO write request, payload: 2B171000D00F</name>
+    <raw>2B171000D00F</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>SDO_REQUEST</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>INITIALIZE_DOWNLOAD</command>
+        <expedited>true</expedited>
+        <placement>true</placement>
+        <index>4119</index>
+        <subindex>0</subindex>
+        <data>
+          <data>208</data>
+          <data>15</data>
+        </data>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>SDO write result, payload: 60171000D0F00000</name>
+    <raw>00171000A00F0000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>SDO_REQUEST</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+        <command>SEGMENT_DOWNLOAD</command>
+        <expedited>false</expedited>
+        <placement>false</placement>
+        <index>4119</index>
+        <subindex>0</subindex>
+        <data/>
+      </CANOpenSDORequest>
+    </xml>
+  </testcase>
+
+  <!-- samples -->
+  <testcase>
+    <name>SDO response, payload: 6000200000</name>
+    <raw>6000200000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>SDO_RESPONSE</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <state>PRE_OPERATIONAL</state>
+      </CANOpenNetworkPayload>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>PDO, payload: 78563412</name>
+    <raw>78563412</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>SDO_RESPONSE</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <state>PRE_OPERATIONAL</state>
+      </CANOpenNetworkPayload>
+    </xml>
+
+  </testcase>
+
 </test:testsuite>
\ No newline at end of file


[plc4x] 07/21: More work on socketcan transport.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit b2b0bd37e23644d10b7e8d9a9c4afc71bb2b8a4a
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Fri Aug 28 12:05:30 2020 +0200

    More work on socketcan transport.
---
 .../socketcan/netty/SocketCANChannel.java          |  3 +-
 .../main/resources/protocols/can/socketcan.mspec   |  1 +
 .../apache/plc4x/java/can/CANOpenPlcDriver.java    |  4 +-
 .../java/can/configuration/CANConfiguration.java   | 11 +++++
 .../java/can/protocol/CANOpenProtocolLogic.java    | 49 +++++++++++++++++++++-
 .../services/org.apache.plc4x.java.api.PlcDriver   |  1 +
 .../test/java/org/apache/plc4x/java/can/Main.java  |  2 +-
 .../testsuite/CANOpenPayloadTestSuite.xml          | 29 ++++++++-----
 .../resources/testsuite/SocketCANTestSuite.xml     |  4 +-
 9 files changed, 86 insertions(+), 18 deletions(-)

diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
index 764d1f5..305d452 100644
--- a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/netty/SocketCANChannel.java
@@ -243,7 +243,8 @@ public class SocketCANChannel extends OioByteStreamChannel {
 
         @Override
         public void write(byte[] b, int off, int len) throws IOException {
-            ByteBuffer buffer = ByteBuffer.wrap(b, off, len);
+            ByteBuffer buffer = ByteBuffer.allocateDirect(len - off);
+            buffer.put(b, off, len);
             CanFrame frame = CanFrame.create(buffer);
             rawCanChannel.write(frame);
         }
diff --git a/protocols/can/src/main/resources/protocols/can/socketcan.mspec b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
index e2e9e3b..e657a9d 100644
--- a/protocols/can/src/main/resources/protocols/can/socketcan.mspec
+++ b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
@@ -58,6 +58,7 @@
     [reserved uint 8 '0x0'] // padding 1
     [reserved uint 8 '0x0'] // padding 2
     [array int 8 'data' COUNT 'size']
+    [padding  uint 8 'pad' '0x00' '8 - (COUNT(data))']
 ]
 
 [type 'SocketCAN20AFrame'
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
index b1c6fe6..4255ff2 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
@@ -42,12 +42,12 @@ public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
 
     @Override
     public String getProtocolCode() {
-        return "can";
+        return "canopen";
     }
 
     @Override
     public String getProtocolName() {
-        return "Controller Area Network";
+        return "CANopen";
     }
 
     @Override
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
index 1a86e45..925ca15 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/configuration/CANConfiguration.java
@@ -19,8 +19,19 @@ under the License.
 package org.apache.plc4x.java.can.configuration;
 
 import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
 import org.apache.plc4x.java.transport.socketcan.CANTransportConfiguration;
 
 public class CANConfiguration implements Configuration, CANTransportConfiguration {
 
+    @ConfigurationParameter
+    private int nodeId;
+
+    public int getNodeId() {
+        return nodeId;
+    }
+
+    public void setNodeId(int nodeId) {
+        this.nodeId = nodeId;
+    }
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index e6f18ca..555ef92 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -18,23 +18,62 @@ under the License.
 */
 package org.apache.plc4x.java.can.protocol;
 
+import org.apache.plc4x.java.can.configuration.CANConfiguration;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.io.CANOpenNetworkPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.canopen.readwrite.types.NMTState;
 import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.configuration.HasConfiguration;
+import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> {
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> implements HasConfiguration<CANConfiguration> {
 
     private Logger logger = LoggerFactory.getLogger(CANOpenProtocolLogic.class);
 
+    private CANConfiguration configuration;
+    private RequestTransactionManager tm;
+
+    @Override
+    public void setConfiguration(CANConfiguration configuration) {
+        this.configuration = configuration;
+        // Set the transaction manager to allow only one message at a time.
+        this.tm = new RequestTransactionManager(1);
+    }
+
     @Override
     public void onConnect(ConversationContext<SocketCANFrame> context) {
-        context.fireConnected();
+        CANOpenNetworkPayload state = new CANOpenNetworkPayload(NMTState.BOOTED_UP);
+        WriteBuffer buffer = new WriteBuffer(1);
+        try {
+            CANOpenNetworkPayloadIO.staticSerialize(buffer, state);
+            context.sendToWire(new SocketCANFrame(cobId(CANOpenService.NMT), buffer.getData()));
+            context.fireConnected();
+
+            Timer heartbeat = new Timer();
+            heartbeat.scheduleAtFixedRate(new TimerTask() {
+                @Override
+                public void run() {
+                    CANOpenNetworkPayload state = new CANOpenNetworkPayload(NMTState.OPERATIONAL);
+                    WriteBuffer buffer = new WriteBuffer(1);
+                    context.sendToWire(new SocketCANFrame(cobId(CANOpenService.NMT), buffer.getData()));
+                }
+            }, 5000, 5000);
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
     }
 
     @Override
@@ -46,6 +85,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> {
         if (service != null) {
             ReadBuffer buffer = new ReadBuffer(msg.getData());
             CANOpenPayload payload = CANOpenPayloadIO.staticParse(buffer, service);
+
+
         }
     }
 
@@ -59,4 +100,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> {
 
     }
 
+    private int cobId(CANOpenService service) {
+        return service.getValue() & configuration.getNodeId();
+    }
+
 }
diff --git a/sandbox/test-java-can-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver b/sandbox/test-java-can-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
index cb3d2ec..3bbef56 100644
--- a/sandbox/test-java-can-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
+++ b/sandbox/test-java-can-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
@@ -17,3 +17,4 @@
 # under the License.
 #
 org.apache.plc4x.java.can.CANPlcDriver
+org.apache.plc4x.java.can.CANOpenPlcDriver
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
index 622f602..e8279b6 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/Main.java
@@ -29,7 +29,7 @@ public class Main {
     public static void main(String[] args) throws Exception {
         PlcDriverManager driverManager = new PlcDriverManager();
 
-        PlcConnection connection = driverManager.getConnection("can:javacan://vcan0");
+        PlcConnection connection = driverManager.getConnection("canopen:javacan://vcan0?nodeId=15");
 
     }
 
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
index b5676d9..773f608 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
@@ -22,21 +22,30 @@
   <name>Tests of socketcan/CANopen frames.</name>
 
   <testcase>
-    <name>Network heartbeat payload: 0000</name>
-    <raw>0000</raw>
+    <name>Network heartbeat payload: 00</name>
+    <raw>00</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
       <arg1>NMT</arg1>
     </parser-arguments>
     <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.canopen.readwrite.CANOpenPayload">
-        <function>15</function>
-        <identifier>5</identifier>
-        <extended>false</extended>
-        <remote>false</remote>
-        <error>false</error>
-        <data>ESIzRFVmd4g=</data>
-      </SocketCANFrame>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <state>BOOTED_UP</state>
+      </CANOpenNetworkPayload>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>Network heartbeat payload: 7F</name>
+    <raw>7F</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>NMT</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <state>PRE_OPERATIONAL</state>
+      </CANOpenNetworkPayload>
     </xml>
   </testcase>
 
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
index 03eaaf1..84fc15e 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/SocketCANTestSuite.xml
@@ -39,7 +39,7 @@
 
   <testcase>
     <name>Standard frame with no payload: 5A1#</name>
-    <raw>a105000000000000</raw>
+    <raw>a1050000000000000000000000000000</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
       <SocketCANFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame">
@@ -55,7 +55,7 @@
 
   <testcase>
     <name>Remote transmission request: 5A1#R</name>
-    <raw>a105004000000000</raw>
+    <raw>a1050040000000000000000000000000</raw>
     <root-type>SocketCANFrame</root-type>
     <xml>
       <SocketCANFrame className="org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame">


[plc4x] 20/21: Support for NMT subscriptions.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 13ebb2aabcd838238b3178c066026c7d94b2c6a3
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Mon Oct 19 00:33:20 2020 +0200

    Support for NMT subscriptions.
---
 ...ndle.java => CANOpenPDOSubscriptionHandle.java} |  6 +--
 .../java/can/protocol/CANOpenProtocolLogic.java    | 56 +++++++++++++++-------
 2 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenPDOSubscriptionHandle.java
similarity index 71%
rename from sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
rename to sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenPDOSubscriptionHandle.java
index 9bfd688..80876c6 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenPDOSubscriptionHandle.java
@@ -5,11 +5,11 @@ import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.spi.messages.PlcSubscriber;
 import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
 
-public class CANOpenSubscriptionHandle extends DefaultPlcSubscriptionHandle {
+public class CANOpenPDOSubscriptionHandle extends DefaultPlcSubscriptionHandle {
     private final String name;
     private final CANOpenPDOField field;
 
-    public CANOpenSubscriptionHandle(PlcSubscriber subscriber, String name, CANOpenPDOField field) {
+    public CANOpenPDOSubscriptionHandle(PlcSubscriber subscriber, String name, CANOpenPDOField field) {
         super(subscriber);
         this.name = name;
         this.field = field;
@@ -31,7 +31,7 @@ public class CANOpenSubscriptionHandle extends DefaultPlcSubscriptionHandle {
     }
 
     public String toString() {
-        return "CANOpenSubscriptionHandle [service=" + field.getService() + ", node=" + intAndHex(field.getNodeId()) + ", cob=" + intAndHex(field.getService().getMin() + field.getNodeId()) + "]";
+        return "CANOpenPDOSubscriptionHandle [service=" + field.getService() + ", node=" + intAndHex(field.getNodeId()) + ", cob=" + intAndHex(field.getService().getMin() + field.getNodeId()) + "]";
     }
 
     private static String intAndHex(int val) {
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index 96488ec..5285b16 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -19,28 +19,27 @@ under the License.
 package org.apache.plc4x.java.can.protocol;
 
 import org.apache.commons.codec.binary.Hex;
-import org.apache.plc4x.java.api.exceptions.PlcException;
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
 import org.apache.plc4x.java.api.model.PlcField;
 import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.api.types.PlcSubscriptionType;
-import org.apache.plc4x.java.api.value.PlcList;
 import org.apache.plc4x.java.api.value.PlcNull;
+import org.apache.plc4x.java.api.value.PlcStruct;
+import org.apache.plc4x.java.api.value.PlcUSINT;
 import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.can.canopen.CANOpenFrame;
 import org.apache.plc4x.java.can.api.conversation.canopen.CANConversation;
 import org.apache.plc4x.java.can.api.conversation.canopen.CANOpenConversation;
 import org.apache.plc4x.java.can.api.conversation.canopen.SDODownloadConversation;
 import org.apache.plc4x.java.can.api.conversation.canopen.SDOUploadConversation;
-import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilder;
 import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilderFactory;
 import org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrameBuilder;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.can.context.CANOpenDriverContext;
 import org.apache.plc4x.java.can.field.CANOpenField;
+import org.apache.plc4x.java.can.field.CANOpenNMTField;
 import org.apache.plc4x.java.can.field.CANOpenPDOField;
 import org.apache.plc4x.java.can.field.CANOpenSDOField;
 import org.apache.plc4x.java.can.socketcan.SocketCANConversation;
@@ -49,7 +48,6 @@ import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.canopen.readwrite.types.NMTState;
-import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.configuration.HasConfiguration;
@@ -60,6 +58,7 @@ import org.apache.plc4x.java.spi.generation.WriteBuffer;
 import org.apache.plc4x.java.spi.messages.*;
 import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
+import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
 import org.apache.plc4x.java.spi.model.InternalPlcSubscriptionHandle;
 import org.apache.plc4x.java.spi.model.SubscriptionPlcField;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
@@ -72,7 +71,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
@@ -251,12 +249,16 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
             SubscriptionPlcField subscription = entry.getValue();
             if (subscription.getPlcSubscriptionType() != PlcSubscriptionType.EVENT) {
                 answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.UNSUPPORTED, null));
-            } else if (!(subscription.getPlcField() instanceof CANOpenPDOField)) {
-                answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.INVALID_ADDRESS, null));
-            } else {
+            } else if ((subscription.getPlcField() instanceof CANOpenPDOField)) {
+                answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.OK,
+                    new CANOpenPDOSubscriptionHandle(this, entry.getKey(), (CANOpenPDOField) subscription.getPlcField())
+                ));
+            } else if ((subscription.getPlcField() instanceof CANOpenNMTField)) {
                 answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.OK,
-                    new CANOpenSubscriptionHandle(this, entry.getKey(), (CANOpenPDOField) subscription.getPlcField())
+                    new CANOpenNMTSubscriptionHandle(this, entry.getKey(), (CANOpenNMTField) subscription.getPlcField())
                 ));
+            } else {
+                answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.INVALID_ADDRESS, null));
             }
         }
 
@@ -295,6 +297,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
         if (service != null) {
             if (service.getPdo() && payload instanceof CANOpenPDOPayload) {
                 publishEvent(service, nodeId, (CANOpenPDOPayload) payload);
+            } else if (service == CANOpenService.NMT && payload instanceof CANOpenNetworkPayload) {
+                publishEvent(service, nodeId, (CANOpenNetworkPayload) payload);
             } else {
                 String hex = "";
                 if (logger.isInfoEnabled()) {
@@ -320,22 +324,22 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
 //        }
     }
 
-    private void publishEvent(CANOpenService service, int nodeId, CANOpenPDOPayload payload) {
-        CANOpenSubscriptionHandle dispatchedHandle = null;
+    private void publishEvent(CANOpenService service, int nodeId, CANOpenPayload payload) {
+        DefaultPlcSubscriptionHandle dispatchedHandle = null;
         for (Map.Entry<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> entry : consumers.entrySet()) {
             DefaultPlcConsumerRegistration registration = entry.getKey();
             Consumer<PlcSubscriptionEvent> consumer = entry.getValue();
 
             for (InternalPlcSubscriptionHandle handler : registration.getAssociatedHandles()) {
-                if (handler instanceof CANOpenSubscriptionHandle) {
-                    CANOpenSubscriptionHandle handle = (CANOpenSubscriptionHandle) handler;
+                if (handler instanceof CANOpenPDOSubscriptionHandle && payload instanceof CANOpenPDOPayload) {
+                    CANOpenPDOSubscriptionHandle handle = (CANOpenPDOSubscriptionHandle) handler;
 
                     if (handle.matches(service, nodeId)) {
                         logger.trace("Dispatching notification {} for node {} to {}", service, nodeId, handle);
                         dispatchedHandle = handle;
 
                         CANOpenPDOField field = handle.getField();
-                        byte[] data = payload.getPdo().getData();
+                        byte[] data = ((CANOpenPDOPayload) payload).getPdo().getData();
                         try {
                             PlcValue value = DataItemIO.staticParse(new ReadBuffer(data, true), field.getCanOpenDataType(), data.length);
                             DefaultPlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(
@@ -357,7 +361,27 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
                             );
                             consumer.accept(event);
                         }
-                    } else {
+                    }
+                } else if (handler instanceof CANOpenPDOSubscriptionHandle && payload instanceof CANOpenHeartbeatPayload) {
+                    CANOpenNMTSubscriptionHandle handle = (CANOpenNMTSubscriptionHandle) handler;
+
+                    if (handle.matches(service, nodeId)) {
+                        logger.trace("Dispatching notification {} for node {} to {}", service, nodeId, handle);
+                        dispatchedHandle = handle;
+
+                        final NMTState state = ((CANOpenHeartbeatPayload) payload).getState();
+                        Map<String, PlcValue> fields = new HashMap<>();
+                        fields.put("state", new PlcUSINT(state.getValue()));
+                        fields.put("node", new PlcUSINT(nodeId));
+                        PlcStruct struct = new PlcStruct(fields);
+                        DefaultPlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(
+                            Instant.now(),
+                            Collections.singletonMap(
+                                handle.getName(),
+                                new ResponseItem<>(PlcResponseCode.OK, struct)
+                            )
+                        );
+                        consumer.accept(event);
                     }
                 }
             }


[plc4x] 21/21: Support for NMT and HEARTBEAT subscriptions.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 94fb0cb16e68c81c2ce7f62aa53eb159fbbcd150
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Wed Oct 21 10:42:11 2020 +0200

    Support for NMT and HEARTBEAT subscriptions.
---
 sandbox/test-java-can-driver/pom.xml               |  13 +
 .../api/segmentation/accumulator/ByteStorage.java  |  18 ++
 .../apache/plc4x/java/can/field/CANOpenField.java  |   4 +
 ...penNMTField.java => CANOpenHeartbeatField.java} |  18 +-
 .../plc4x/java/can/field/CANOpenNMTField.java      |  10 +-
 .../plc4x/java/can/field/CANOpenPDOField.java      |   8 +-
 .../java/can/field/CANOpenSubscriptionField.java   |  11 +
 .../java/can/protocol/CANOpenProtocolLogic.java    |  51 +++-
 ...nHandle.java => CANOpenSubscriptionHandle.java} |  13 +-
 .../plc4x/java/can/field/CANOpenNMTFieldTest.java  |   9 +-
 .../plc4x/java/can/field/CANOpenSDOFieldTest.java  |  26 ++
 .../resources/testsuite/CANOpenDriverSDOIT.xml     | 324 +++++++++++++++++++++
 12 files changed, 472 insertions(+), 33 deletions(-)

diff --git a/sandbox/test-java-can-driver/pom.xml b/sandbox/test-java-can-driver/pom.xml
index 4ed0eaa..fcb4507 100644
--- a/sandbox/test-java-can-driver/pom.xml
+++ b/sandbox/test-java-can-driver/pom.xml
@@ -29,6 +29,7 @@
   <artifactId>test-java-can-driver</artifactId>
 
   <name>Sandbox: Test Generated CAN Driver</name>
+  <packaging>bundle</packaging>
 
   <build>
     <plugins>
@@ -62,6 +63,18 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+            <Bundle-Activator>org.apache.plc4x.java.osgi.DriverActivator</Bundle-Activator>
+            <Export-Service>org.apache.plc4x.java.api.PlcDriver,org.apache.plc4x.java.can.CANOpenPlcDriver</Export-Service>
+          </instructions>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
 
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/ByteStorage.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/ByteStorage.java
index 1f143f2..ae0a6b4 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/ByteStorage.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/api/segmentation/accumulator/ByteStorage.java
@@ -56,4 +56,22 @@ public class ByteStorage<T> implements Storage<T, byte[]> {
         }
     }
 
+    public static class SDODownloadStorage extends ByteStorage<SDORequest> {
+        public SDODownloadStorage() {
+            super((sdoRequest -> {
+                if (sdoRequest instanceof SDOSegmentDownloadRequest) {
+                    return ((SDOSegmentDownloadRequest) sdoRequest).getData();
+                }
+                if (sdoRequest instanceof  SDOInitiateDownloadRequest) {
+                    SDOInitiateDownloadRequest initiate = (SDOInitiateDownloadRequest) sdoRequest;
+
+                    if (initiate.getPayload() instanceof SDOInitiateExpeditedUploadResponse) {
+                        return ((SDOInitiateExpeditedUploadResponse) initiate.getPayload()).getData();
+                    }
+                }
+                return new byte[0];
+            }));
+        }
+    }
+
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
index 6917351..ddd2a1a 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenField.java
@@ -46,6 +46,10 @@ public abstract class CANOpenField implements PlcField {
             return CANOpenSDOField.of(addressString);
         } else if (CANOpenPDOField.matches(addressString)) {
             return CANOpenPDOField.of(addressString);
+        } else if (CANOpenNMTField.matches(addressString)) {
+            return CANOpenNMTField.of(addressString);
+        } else if (CANOpenHeartbeatField.matches(addressString)) {
+            return CANOpenHeartbeatField.of(addressString);
         }
 
         throw new PlcInvalidFieldException("Unable to parse address: " + addressString);
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenHeartbeatField.java
similarity index 73%
copy from sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java
copy to sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenHeartbeatField.java
index 83bba15..ef6a4ad 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenHeartbeatField.java
@@ -19,18 +19,24 @@ under the License.
 package org.apache.plc4x.java.can.field;
 
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class CANOpenNMTField extends CANOpenField {
+public class CANOpenHeartbeatField extends CANOpenField implements CANOpenSubscriptionField {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("NMT|NMT:" + CANOpenField.NODE_PATTERN);
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("HEARTBEAT|HEARTBEAT:" + CANOpenField.NODE_PATTERN);
 
-    public CANOpenNMTField(int node) {
+    public CANOpenHeartbeatField(int node) {
         super(node);
     }
 
+    @Override
+    public CANOpenService getService() {
+        return CANOpenService.HEARTBEAT;
+    }
+
     public boolean isWildcard() {
         return getNodeId() == 0;
     }
@@ -48,11 +54,11 @@ public class CANOpenNMTField extends CANOpenField {
         throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN);
     }
 
-    public static CANOpenNMTField of(String addressString) {
+    public static CANOpenHeartbeatField of(String addressString) {
         Matcher matcher = getMatcher(addressString);
-        int nodeId = Integer.parseInt(matcher.group("nodeId"));
+        int nodeId = matcher.group("nodeId") == null ? 0 : Integer.parseInt(matcher.group("nodeId"));
 
-        return new CANOpenNMTField(nodeId);
+        return new CANOpenHeartbeatField(nodeId);
     }
 
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java
index 83bba15..74370b1 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenNMTField.java
@@ -19,11 +19,12 @@ under the License.
 package org.apache.plc4x.java.can.field;
 
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class CANOpenNMTField extends CANOpenField {
+public class CANOpenNMTField extends CANOpenField implements CANOpenSubscriptionField {
 
     public static final Pattern ADDRESS_PATTERN = Pattern.compile("NMT|NMT:" + CANOpenField.NODE_PATTERN);
 
@@ -31,6 +32,11 @@ public class CANOpenNMTField extends CANOpenField {
         super(node);
     }
 
+    @Override
+    public CANOpenService getService() {
+        return CANOpenService.NMT;
+    }
+
     public boolean isWildcard() {
         return getNodeId() == 0;
     }
@@ -50,7 +56,7 @@ public class CANOpenNMTField extends CANOpenField {
 
     public static CANOpenNMTField of(String addressString) {
         Matcher matcher = getMatcher(addressString);
-        int nodeId = Integer.parseInt(matcher.group("nodeId"));
+        int nodeId = matcher.group("nodeId") == null ? 0 : Integer.parseInt(matcher.group("nodeId"));
 
         return new CANOpenNMTField(nodeId);
     }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
index 2761260..cfe050d 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenPDOField.java
@@ -26,7 +26,7 @@ import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class CANOpenPDOField extends CANOpenField {
+public class CANOpenPDOField extends CANOpenField implements CANOpenSubscriptionField {
 
     public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<pdo>(?:RECEIVE|TRANSMIT)_PDO_[1-4]):" + CANOpenField.NODE_PATTERN + ":(?<canDataType>\\w+)(\\[(?<numberOfElements>\\d)])?");
     private final CANOpenService service;
@@ -74,4 +74,10 @@ public class CANOpenPDOField extends CANOpenField {
     public CANOpenService getService() {
         return service;
     }
+
+    @Override
+    public boolean isWildcard() {
+        return false;
+    }
+
 }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenSubscriptionField.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenSubscriptionField.java
new file mode 100644
index 0000000..78fafc0
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/field/CANOpenSubscriptionField.java
@@ -0,0 +1,11 @@
+package org.apache.plc4x.java.can.field;
+
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+
+public interface CANOpenSubscriptionField {
+    CANOpenService getService();
+
+    boolean isWildcard();
+
+    int getNodeId();
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index 5285b16..a46b17d 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -38,16 +38,14 @@ import org.apache.plc4x.java.can.canopen.CANOpenFrameBuilderFactory;
 import org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrameBuilder;
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
 import org.apache.plc4x.java.can.context.CANOpenDriverContext;
-import org.apache.plc4x.java.can.field.CANOpenField;
-import org.apache.plc4x.java.can.field.CANOpenNMTField;
-import org.apache.plc4x.java.can.field.CANOpenPDOField;
-import org.apache.plc4x.java.can.field.CANOpenSDOField;
+import org.apache.plc4x.java.can.field.*;
 import org.apache.plc4x.java.can.socketcan.SocketCANConversation;
 import org.apache.plc4x.java.canopen.readwrite.*;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.canopen.readwrite.types.NMTState;
+import org.apache.plc4x.java.canopen.readwrite.types.NMTStateRequest;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.configuration.HasConfiguration;
@@ -251,11 +249,15 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
                 answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.UNSUPPORTED, null));
             } else if ((subscription.getPlcField() instanceof CANOpenPDOField)) {
                 answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.OK,
-                    new CANOpenPDOSubscriptionHandle(this, entry.getKey(), (CANOpenPDOField) subscription.getPlcField())
+                    new CANOpenSubscriptionHandle(this, entry.getKey(), (CANOpenPDOField) subscription.getPlcField())
                 ));
             } else if ((subscription.getPlcField() instanceof CANOpenNMTField)) {
                 answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.OK,
-                    new CANOpenNMTSubscriptionHandle(this, entry.getKey(), (CANOpenNMTField) subscription.getPlcField())
+                    new CANOpenSubscriptionHandle(this, entry.getKey(), (CANOpenNMTField) subscription.getPlcField())
+                ));
+            } else if ((subscription.getPlcField() instanceof CANOpenHeartbeatField)) {
+                answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.OK,
+                    new CANOpenSubscriptionHandle(this, entry.getKey(), (CANOpenHeartbeatField) subscription.getPlcField())
                 ));
             } else {
                 answers.put(entry.getKey(), new ResponseItem<>(PlcResponseCode.INVALID_ADDRESS, null));
@@ -294,11 +296,11 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
         CANOpenService service = msg.getService();
         CANOpenPayload payload = msg.getPayload();
 
-        if (service != null) {
+        if (service != null && nodeId != this.configuration.getNodeId()) {
             if (service.getPdo() && payload instanceof CANOpenPDOPayload) {
-                publishEvent(service, nodeId, (CANOpenPDOPayload) payload);
-            } else if (service == CANOpenService.NMT && payload instanceof CANOpenNetworkPayload) {
-                publishEvent(service, nodeId, (CANOpenNetworkPayload) payload);
+                publishEvent(service, nodeId, payload);
+            } else if (service == CANOpenService.HEARTBEAT && payload instanceof CANOpenHeartbeatPayload) {
+                publishEvent(service, nodeId, payload);
             } else {
                 String hex = "";
                 if (logger.isInfoEnabled()) {
@@ -331,14 +333,14 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
             Consumer<PlcSubscriptionEvent> consumer = entry.getValue();
 
             for (InternalPlcSubscriptionHandle handler : registration.getAssociatedHandles()) {
-                if (handler instanceof CANOpenPDOSubscriptionHandle && payload instanceof CANOpenPDOPayload) {
-                    CANOpenPDOSubscriptionHandle handle = (CANOpenPDOSubscriptionHandle) handler;
+                CANOpenSubscriptionHandle handle = (CANOpenSubscriptionHandle) handler;
+                if (payload instanceof CANOpenPDOPayload) {
 
                     if (handle.matches(service, nodeId)) {
                         logger.trace("Dispatching notification {} for node {} to {}", service, nodeId, handle);
                         dispatchedHandle = handle;
 
-                        CANOpenPDOField field = handle.getField();
+                        CANOpenPDOField field = (CANOpenPDOField) handle.getField();
                         byte[] data = ((CANOpenPDOPayload) payload).getPdo().getData();
                         try {
                             PlcValue value = DataItemIO.staticParse(new ReadBuffer(data, true), field.getCanOpenDataType(), data.length);
@@ -362,9 +364,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
                             consumer.accept(event);
                         }
                     }
-                } else if (handler instanceof CANOpenPDOSubscriptionHandle && payload instanceof CANOpenHeartbeatPayload) {
-                    CANOpenNMTSubscriptionHandle handle = (CANOpenNMTSubscriptionHandle) handler;
-
+                } else if (payload instanceof CANOpenHeartbeatPayload) {
                     if (handle.matches(service, nodeId)) {
                         logger.trace("Dispatching notification {} for node {} to {}", service, nodeId, handle);
                         dispatchedHandle = handle;
@@ -383,6 +383,25 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<CANOpenFrame> implem
                         );
                         consumer.accept(event);
                     }
+                } else if (payload instanceof CANOpenNetworkPayload) {
+                    if (handle.matches(service, nodeId)) {
+                        logger.trace("Dispatching notification {} for node {} to {}", service, nodeId, handle);
+                        dispatchedHandle = handle;
+
+                        final NMTStateRequest state = ((CANOpenNetworkPayload) payload).getRequest();
+                        Map<String, PlcValue> fields = new HashMap<>();
+                        fields.put("state", new PlcUSINT(state.getValue()));
+                        fields.put("node", new PlcUSINT(nodeId));
+                        PlcStruct struct = new PlcStruct(fields);
+                        DefaultPlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(
+                            Instant.now(),
+                            Collections.singletonMap(
+                                handle.getName(),
+                                new ResponseItem<>(PlcResponseCode.OK, struct)
+                            )
+                        );
+                        consumer.accept(event);
+                    }
                 }
             }
         }
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenPDOSubscriptionHandle.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
similarity index 56%
rename from sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenPDOSubscriptionHandle.java
rename to sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
index 80876c6..e318da3 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenPDOSubscriptionHandle.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenSubscriptionHandle.java
@@ -1,15 +1,16 @@
 package org.apache.plc4x.java.can.protocol;
 
 import org.apache.plc4x.java.can.field.CANOpenPDOField;
+import org.apache.plc4x.java.can.field.CANOpenSubscriptionField;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
 import org.apache.plc4x.java.spi.messages.PlcSubscriber;
 import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
 
-public class CANOpenPDOSubscriptionHandle extends DefaultPlcSubscriptionHandle {
+public class CANOpenSubscriptionHandle extends DefaultPlcSubscriptionHandle {
     private final String name;
-    private final CANOpenPDOField field;
+    private final CANOpenSubscriptionField field;
 
-    public CANOpenPDOSubscriptionHandle(PlcSubscriber subscriber, String name, CANOpenPDOField field) {
+    public CANOpenSubscriptionHandle(PlcSubscriber subscriber, String name, CANOpenSubscriptionField field) {
         super(subscriber);
         this.name = name;
         this.field = field;
@@ -19,19 +20,19 @@ public class CANOpenPDOSubscriptionHandle extends DefaultPlcSubscriptionHandle {
         if (field.getService() != service) {
             return false;
         }
-        return field.getNodeId() == identifier;
+        return field.isWildcard() || field.getNodeId() == identifier;
     }
 
     public String getName() {
         return name;
     }
 
-    public CANOpenPDOField getField() {
+    public CANOpenSubscriptionField getField() {
         return field;
     }
 
     public String toString() {
-        return "CANOpenPDOSubscriptionHandle [service=" + field.getService() + ", node=" + intAndHex(field.getNodeId()) + ", cob=" + intAndHex(field.getService().getMin() + field.getNodeId()) + "]";
+        return "CANopenSubscriptionHandle [service=" + field.getService() + ", node=" + intAndHex(field.getNodeId()) + ", cob=" + intAndHex(field.getService().getMin() + field.getNodeId()) + "]";
     }
 
     private static String intAndHex(int val) {
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenNMTFieldTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenNMTFieldTest.java
index 3f48fc1..06db074 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenNMTFieldTest.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenNMTFieldTest.java
@@ -35,12 +35,17 @@ class CANOpenNMTFieldTest {
 
     @Test
     public void testWildcardSyntax() {
-        final CANOpenNMTField canField = CANOpenNMTField.of("NMT:0");
+        CANOpenNMTField canField = CANOpenNMTField.of("NMT:0");
 
         assertEquals(0, canField.getNodeId());
         assertTrue(canField.isWildcard());
-    }
 
+        // an simplified syntax
+        canField = CANOpenNMTField.of("NMT");
+
+        assertEquals(0, canField.getNodeId());
+        assertTrue(canField.isWildcard());
+    }
 
     @Test
     public void testInvalidSyntax() {
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenSDOFieldTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenSDOFieldTest.java
new file mode 100644
index 0000000..ddecd2b
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/field/CANOpenSDOFieldTest.java
@@ -0,0 +1,26 @@
+package org.apache.plc4x.java.can.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenDataType;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CANOpenSDOFieldTest {
+
+    @Test
+    public void testNodeSyntax() {
+        final CANOpenSDOField canField = CANOpenSDOField.of("SDO:20:0x10/0xAA:RECORD");
+
+        assertEquals(20, canField.getNodeId());
+        assertEquals(0x10, canField.getIndex());
+        assertEquals(0xAA, canField.getSubIndex());
+        assertEquals(CANOpenDataType.RECORD, canField.getCanOpenDataType());
+    }
+
+    @Test
+    public void testInvalidSyntax() {
+        assertThrows(PlcInvalidFieldException.class, () -> CANOpenSDOField.of("SDO:"));
+    }
+
+}
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenDriverSDOIT.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenDriverSDOIT.xml
new file mode 100644
index 0000000..190f3be
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenDriverSDOIT.xml
@@ -0,0 +1,324 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<test:driver-testsuite xmlns:test="https://plc4x.apache.org/schemas/driver-testsuite.xsd"
+                       bigEndian="false">
+
+  <name>CANOpen SDO Segmentation</name>
+
+  <driver-name>canopen</driver-name>
+  <driver-parameters>
+    <parameter>
+      <name>nodeId</name>
+      <value>15</value>
+    </parameter>
+  </driver-parameters>
+
+  <testcase>
+    <name>Expedited SDO read request</name>
+    <description>
+      Single field read request which answers with 4 bytes of data.
+    </description>
+    <steps>
+      <api-request name="Receive Read Request from application">
+        <TestReadRequest className="org.apache.plc4x.test.driver.model.api.TestReadRequest">
+          <fields>
+            <field className="org.apache.plc4x.test.driver.model.api.TestField">
+              <name>sdo1</name>
+              <address>SDO:1:1000/22:UNSIGNED32</address>
+            </field>
+          </fields>
+        </TestReadRequest>
+      </api-request>
+      <outgoing-plc-message name="Send SDO Initialize Upload Request">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>1</nodeId>
+          <service>RECEIVE_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+            <command>INITIATE_UPLOAD</command>
+            <request className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadRequest">
+              <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+                <index>1000</index>
+                <subindex>22</subindex>
+              </address>
+            </request>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive SDO Initialize Upload Response for other node">
+        <!-- one unwanted frame -->
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>1</nodeId>
+          <service>TRANSMIT_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+            <command>INITIATE_UPLOAD</command>
+            <response className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadResponse">
+              <expedited>true</expedited>
+              <indicated>true</indicated>
+              <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+                <index>1001</index>
+                <subindex>22</subindex>
+              </address>
+              <payload className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateExpeditedUploadResponse">
+                <data>YXNkZg==</data>
+              </payload>
+            </response>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </incoming-plc-message>
+      <incoming-plc-message name="Receive SDO Initialize Upload Response for from requested node">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>1</nodeId>
+          <service>TRANSMIT_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+            <command>INITIATE_UPLOAD</command>
+            <response className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadResponse">
+              <expedited>true</expedited>
+              <indicated>true</indicated>
+              <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+                <index>1000</index>
+                <subindex>22</subindex>
+              </address>
+              <payload className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateExpeditedUploadResponse">
+                <data>YXNkZg==</data>
+              </payload>
+            </response>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </incoming-plc-message>
+      <api-response name="Report Read Response to application">
+        <DefaultPlcReadResponse className="org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse">
+          <request className="org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest">
+            <sdo1 className="org.apache.plc4x.java.can.field.CANOpenSDOField">
+              <nodeId>1</nodeId>
+              <index>1000</index>
+              <subIndex>22</subIndex>
+              <canOpenDataType>UNSIGNED32</canOpenDataType>
+            </sdo1>
+          </request>
+          <sdo1>
+            <code>OK</code>
+            <value className="org.apache.plc4x.java.api.value.PlcUDINT">
+              <object>java.lang.Long</object>
+              <object>1717859169</object>
+            </value>
+          </sdo1>
+        </DefaultPlcReadResponse>
+      </api-response>
+      <delay>1000</delay>
+    </steps>
+  </testcase>
+
+  <testcase>
+    <name>Segmented SDO read request</name>
+    <description>
+      Single field read request which answers with 8 bytes of data which must go over two CAN frames.
+    </description>
+    <steps>
+      <api-request name="Receive Read Request from application">
+        <TestReadRequest className="org.apache.plc4x.test.driver.model.api.TestReadRequest">
+          <fields>
+            <field className="org.apache.plc4x.test.driver.model.api.TestField">
+              <name>sdo1</name>
+              <address>SDO:2:2000/44:RECORD</address>
+            </field>
+          </fields>
+        </TestReadRequest>
+      </api-request>
+
+      <outgoing-plc-message name="Send SDO Initialize Upload Request">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>RECEIVE_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+            <command>INITIATE_UPLOAD</command>
+            <request className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadRequest">
+              <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+                <index>2000</index>
+                <subindex>44</subindex>
+              </address>
+            </request>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive SDO Initialize Upload Response with segment information">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>TRANSMIT_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+            <command>INITIATE_UPLOAD</command>
+            <response className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateUploadResponse">
+              <expedited>false</expedited>
+              <indicated>true</indicated>
+              <address className="org.apache.plc4x.java.canopen.readwrite.IndexAddress">
+                <index>2000</index>
+                <subindex>44</subindex>
+              </address>
+              <payload className="org.apache.plc4x.java.canopen.readwrite.SDOInitiateSegmentedUploadResponse">
+                <bytes>8</bytes>
+              </payload>
+            </response>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </incoming-plc-message>
+
+      <outgoing-plc-message name="Send first SDO Segment Request">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>RECEIVE_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+            <command>SEGMENT_UPLOAD</command>
+            <request className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadRequest">
+              <toggle>false</toggle>
+            </request>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive SDO Initialize Upload Response for first segment">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>TRANSMIT_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+            <command>SEGMENT_UPLOAD</command>
+            <response className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadResponse">
+              <toggle>false</toggle>
+              <last>false</last>
+              <data>YXNkZg==</data>
+            </response>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </incoming-plc-message>
+
+      <outgoing-plc-message name="Send SDO Initialize Upload Request">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>RECEIVE_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+            <command>SEGMENT_UPLOAD</command>
+            <request className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadRequest">
+              <toggle>true</toggle>
+            </request>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive SDO Initialize Upload Response with segment information">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>TRANSMIT_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+            <command>SEGMENT_UPLOAD</command>
+            <response className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadResponse">
+              <toggle>true</toggle>
+              <last>false</last>
+              <data>YXNkZg==</data>
+            </response>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </incoming-plc-message>
+
+      <outgoing-plc-message name="Send second SDO Segment Request">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>RECEIVE_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
+            <command>SEGMENT_UPLOAD</command>
+            <request className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadRequest">
+              <toggle>false</toggle>
+            </request>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive SDO Initialize Upload Response for second segment">
+        <CANOpenSocketCANFrame className="org.apache.plc4x.java.can.canopen.socketcan.CANOpenSocketCANFrame">
+          <nodeId>2</nodeId>
+          <service>TRANSMIT_SDO</service>
+          <payload className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+            <command>SEGMENT_UPLOAD</command>
+            <response className="org.apache.plc4x.java.canopen.readwrite.SDOSegmentUploadResponse">
+              <toggle>false</toggle>
+              <last>true</last>
+              <data>YXNkZg==</data>
+            </response>
+          </payload>
+        </CANOpenSocketCANFrame>
+      </incoming-plc-message>
+
+      <api-response name="Report Read Response to application">
+        <DefaultPlcReadResponse className="org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse">
+          <request className="org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest">
+            <sdo1 className="org.apache.plc4x.java.can.field.CANOpenSDOField">
+              <nodeId>2</nodeId>
+              <index>2000</index>
+              <subIndex>44</subIndex>
+              <canOpenDataType>RECORD</canOpenDataType>
+            </sdo1>
+          </request>
+          <sdo1>
+            <code>OK</code>
+            <value className="org.apache.plc4x.java.api.value.PlcList">
+              <object>java.util.Collections..UnmodifiableRandomAccessList</object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>97</object>
+              </object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>115</object>
+              </object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>100</object>
+              </object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>102</object>
+              </object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>97</object>
+              </object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>115</object>
+              </object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>100</object>
+              </object>
+              <object>org.apache.plc4x.java.api.value.PlcSINT</object>
+              <object>
+                <object>java.lang.Byte</object>
+                <object>102</object>
+              </object>
+            </value>
+          </sdo1>
+        </DefaultPlcReadResponse>
+      </api-response>
+      <delay>1000</delay>
+    </steps>
+  </testcase>
+
+</test:driver-testsuite>
\ No newline at end of file


[plc4x] 14/21: CANopen adjustments.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 59c7cc6c9bfd446a042af0b7680d5f8f2b452330
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Thu Sep 3 13:46:10 2020 +0200

    CANopen adjustments.
---
 .../src/main/resources/protocols/can/canopen.mspec | 144 ++++++++++++++-------
 sandbox/test-java-can-driver/pom.xml               |   5 +
 .../java/can/protocol/CANOpenProtocolLogic.java    |  12 +-
 .../testsuite/CANOpenPayloadTestSuite.xml          |  71 ++++++----
 4 files changed, 153 insertions(+), 79 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index c929ae4..bb825f3 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -17,21 +17,22 @@
  * under the License.
  */
 
-[enum uint 4 'CANOpenService' [bit 'sdo', bit 'pdo', bit 'transmit', bit 'receive']
-    ['0b0000' BROADCAST    ['false', 'false', 'false', 'false'] ]
-    ['0b0001' SYNC         ['false', 'false', 'false', 'false'] ]
-    ['0b0010' TIME         ['false', 'false', 'false', 'false'] ]
-    ['0b1110' NMT          ['false', 'false', 'false', 'false'] ]
-    ['0b1100' SDO_REQUEST  ['true',  'false', 'false', 'true' ] ]
-    ['0b1011' SDO_RESPONSE ['true',  'false', 'true',  'false'] ]
-    ['0b0011' TPDO_1       ['false', 'true',  'true',  'false'] ]
-    ['0b0100' RPDO_1       ['false', 'true',  'false', 'true' ] ]
-    ['0b0101' TPDO_2       ['false', 'true',  'true',  'false'] ]
-    ['0b0110' RPDO_2       ['false', 'true',  'false', 'true' ] ]
-    ['0b0111' TPDO_3       ['false', 'true',  'true',  'false'] ]
-    ['0b1000' RPDO_3       ['false', 'true',  'false', 'true' ] ]
-    ['0b1001' TPDO_4       ['false', 'true',  'true',  'false'] ]
-    ['0b1010' RPDO_4       ['false', 'true',  'false', 'true' ] ]
+[enum uint 4 'CANOpenService' [uint 8 'min', uint 8 'max', bit 'pdo']
+    ['0b0000' NMT             ['0',     '0'    , 'false' ] ]
+    ['0b0001' SYNC            ['0x80',  '0x80' , 'false' ] ]
+    ['0b0001' EMCY            ['0x81',  '0xFF' , 'false' ] ]
+    ['0b0010' TIME            ['0x100', '0x100', 'false' ] ]
+    ['0b0011' TRANSMIT_PDO_1  ['0x181', '0x1FF', 'true'  ] ]
+    ['0b0100' RECEIVE_PDO_1   ['0x201', '0x27F', 'true'  ] ]
+    ['0b0101' TRANSMIT_PDO_2  ['0x281', '0x2FF', 'true'  ] ]
+    ['0b0110' RECEIVE_PDO_2   ['0x301', '0x37F', 'true'  ] ]
+    ['0b0111' TRANSMIT_PDO_3  ['0x381', '0x3FF', 'true'  ] ]
+    ['0b1000' RECEIVE_PDO_3   ['0x401', '0x47F', 'true'  ] ]
+    ['0b1001' TRANSMIT_PDO_4  ['0x481', '0x4FF', 'true'  ] ]
+    ['0b1010' RECEIVE_PDO_4   ['0x501', '0x57F', 'true'  ] ]
+    ['0b1011' TRANSMIT_SDO    ['0x581', '0x5FF', 'false' ] ]
+    ['0b1100' RECEIVE_SDO     ['0x601', '0x67F', 'false' ] ]
+    ['0b1110' HEARTBEAT       ['0x701', '0x77F', 'false' ] ]
 ]
 
 [enum uint 8 'NMTStateRequest'
@@ -51,7 +52,7 @@
 
 [discriminatedType 'CANOpenPayload' [CANOpenService 'function']
     [typeSwitch 'function'
-        ['CANOpenService.BROADCAST' CANOpenBroadcastPayload
+        ['CANOpenService.NMT' CANOpenNetworkPayload
             [enum NMTStateRequest 'request']
             [reserved uint 1 '0x00']
             [simple uint 7 'node']
@@ -59,56 +60,103 @@
         ['CANOpenService.TIME' CANOpenTimeSynchronization
             [simple TimeOfDay 'timeOfDay']
         ]
-        ['CANOpenService.NMT' CANOpenNetworkPayload
-            [enum NMTState 'state']
-        ]
-        ['CANOpenService.SDO_REQUEST' CANOpenSDORequest
-            [enum SDOCommand 'command']
-            [reserved uint 1 '0x00']
-            [implicit uint 2 'size' 'COUNT(data)']
-            [simple bit 'expedited'] // segmented
-            [simple bit 'placement']
-            [simple uint 16 'index']
-            [simple uint 8 'subindex']
-            [array uint 8 'data' COUNT 'size']
-        ]
-        ['CANOpenService.SDO_RESPONSE' CANOpenSDOResponse
-            [enum SDOCommand 'command']
-            [reserved uint 1 '0x00']
-            [implicit uint 2 'size' 'COUNT(data)']
-            [simple bit 'expedited'] // segmented
-            [simple bit 'placement']
-            [simple uint 16 'index']
-            [simple uint 8 'subindex']
-            [array uint 8 'data' COUNT 'size']
-        ]
-        ['CANOpenService.RPDO_1' CANOpenRPDO
+        ['CANOpenService.RECEIVE_PDO_1' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['1', 'true']]
         ]
-        ['CANOpenService.TPDO_1' CANOpenTPDO
+        ['CANOpenService.TRANSMIT_PDO_1' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['1', 'false']]
         ]
-        ['CANOpenService.RPDO_2' CANOpenRPDO
+        ['CANOpenService.RECEIVE_PDO_2' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['2', 'true']]
         ]
-        ['CANOpenService.TPDO_2' CANOpenTPDO
+        ['CANOpenService.TRANSMIT_PDO_2' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['1', 'false']]
         ]
-        ['CANOpenService.RPDO_3' CANOpenRPDO
+        ['CANOpenService.RECEIVE_PDO_3' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['3', 'true']]
         ]
-        ['CANOpenService.TPDO_3' CANOpenTPDO
+        ['CANOpenService.TRANSMIT_PDO_3' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['1', 'false']]
         ]
-        ['CANOpenService.RPDO_4' CANOpenRPDO
+        ['CANOpenService.RECEIVE_PDO_4' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['4', 'true']]
         ]
-        ['CANOpenService.TPDO_4' CANOpenTPDO
+        ['CANOpenService.TRANSMIT_PDO_4' CANOpenPDOPayload
             [simple CANOpenPDO 'pdo' ['1', 'false']]
         ]
+        ['CANOpenService.TRANSMIT_SDO' CANOpenSDORequest
+            [enum SDOCommand 'command']
+            [simple SDORequest 'request' ['command']]
+        ]
+        ['CANOpenService.RECEIVE_SDO' CANOpenSDOResponse
+            [enum SDOCommand 'command']
+            [simple SDOResponse 'response' ['command']]
+        ]
+        ['CANOpenService.HEARTBEAT' CANOpenHeartbeatPayload
+            [enum NMTState 'state']
+        ]
+    ]
+]
+
+[type 'SDORequest' [SDOCommand 'command']
+    [typeSwitch 'command'
+        ['SDOCommand.INITIALIZE_DOWNLOAD' SDOInitializeDownloadRequest
+            [reserved uint 1 '0x00']
+            [implicit uint 2 'size' 'expedited && indicated ? 4 - COUNT(data) : 0']
+            [simple bit 'expedited']
+            [simple bit 'indicated']
+            [simple Multiplexer 'address']
+            [array int 8 'data' COUNT '(expedited && indicated) ? 4 - size : 0']
+            [padding uint 8 'alignment' '0x00' '4 - (COUNT(data))']
+        ]
+        ['SDOCommand.SEGMENT_DOWNLOAD' SDOSegmentDownloadRequest
+            [simple bit 'toggle']
+            [implicit uint 3 'size' '7 - COUNT(data)']
+            [simple bit 'last']
+            [array int 8 'data' COUNT '7 - data']
+            [padding uint 8 'alignment' '0x00' '7 - (COUNT(data))']
+        ]
+        ['SDOCommand.INITIALIZE_UPLOAD' SDOInitializeUploadRequest
+            [reserved uint 5 '0x00']
+            [simple Multiplexer 'address']
+            [reserved int 32 '0x00'] // padding
+        ]
+    ]
+]
+
+[type 'SDOResponse' [SDOCommand 'command']
+    [typeSwitch 'command'
+        ['SDOCommand.SEGMENT_UPLOAD' SDOSegmentUploadResponse
+            [reserved uint 5 '0x00']
+            [simple Multiplexer 'address']
+            [reserved int 32 '0x00'] // padding
+        ]
+        ['SDOCommand.INITIALIZE_DOWNLOAD' SDOInitializeDownloadResponse
+            [simple bit 'toggle']
+            [reserved uint 4 '0x00']
+            [reserved int 32 '0x00'] // padding
+        ]
+        ['SDOCommand.INITIALIZE_UPLOAD' SDOInitializeUploadResponse
+            [simple SDOSegment 'segment']
+        ]
     ]
 ]
 
+[type 'SDOSegment'
+    [reserved uint 1 '0x00']
+    [implicit uint 2 'size' 'expedited && indicated ? 4 - COUNT(data) : 0']
+    [simple bit 'expedited']
+    [simple bit 'indicated']
+    [simple Multiplexer 'address']
+    [array int 8 'data' COUNT '(expedited && indicated) ? 4 - size : 0']
+    [padding uint 8 'alignment' '0x00' '4 - (COUNT(data))']
+]
+
+[type 'Multiplexer'
+    [simple uint 16 'index']
+    [simple uint 8 'subindex']
+]
+
 [enum uint 3 'SDOCommand'
     ['0x00' SEGMENT_DOWNLOAD]
     ['0x01' INITIALIZE_DOWNLOAD]
@@ -120,7 +168,7 @@
 ]
 
 [type 'CANOpenPDO' [uint 2 'index', bit 'receive']
-
+    [array int 8 'data' COUNT '8']
 ]
 
 [type 'TimeOfDay'
diff --git a/sandbox/test-java-can-driver/pom.xml b/sandbox/test-java-can-driver/pom.xml
index 50e48e7..e3e72c4 100644
--- a/sandbox/test-java-can-driver/pom.xml
+++ b/sandbox/test-java-can-driver/pom.xml
@@ -73,6 +73,11 @@
     </dependency>
 
     <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-configuration2</artifactId>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.plc4x</groupId>
       <artifactId>plc4j-transport-socketcan</artifactId>
       <version>0.8.0-SNAPSHOT</version>
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index 0090e0e..c193ec3 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -19,8 +19,10 @@ under the License.
 package org.apache.plc4x.java.can.protocol;
 
 import org.apache.plc4x.java.can.configuration.CANConfiguration;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.io.CANOpenHeartbeatPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenNetworkPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
@@ -58,7 +60,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
     public void onConnect(ConversationContext<SocketCANFrame> context) {
         try {
             if (configuration.isHeartbeat()) {
-                context.sendToWire(createFrame(new CANOpenNetworkPayload(NMTState.BOOTED_UP)));
+                context.sendToWire(createFrame(new CANOpenHeartbeatPayload(NMTState.BOOTED_UP)));
                 context.fireConnected();
 
                 this.heartbeat = new Timer();
@@ -66,7 +68,7 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
                     @Override
                     public void run() {
                         try {
-                            context.sendToWire(createFrame(new CANOpenNetworkPayload(NMTState.OPERATIONAL)));
+                            context.sendToWire(createFrame(new CANOpenHeartbeatPayload(NMTState.OPERATIONAL)));
                         } catch (ParseException e) {
                             e.printStackTrace();
                         }
@@ -78,10 +80,10 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
         }
     }
 
-    private SocketCANFrame createFrame(CANOpenNetworkPayload state) throws ParseException {
+    private SocketCANFrame createFrame(CANOpenHeartbeatPayload state) throws ParseException {
         WriteBuffer buffer = new WriteBuffer(state.getLengthInBytes());
-        CANOpenNetworkPayloadIO.staticSerialize(buffer, state);
-        return new SocketCANFrame(cobId(CANOpenService.NMT), buffer.getData());
+        CANOpenHeartbeatPayloadIO.staticSerialize(buffer, state);
+        return new SocketCANFrame(cobId(CANOpenService.HEARTBEAT), buffer.getData());
     }
 
     @Override
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
index 3188005..8537aee 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
@@ -67,12 +67,12 @@
     <raw>00</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>NMT</arg1>
+      <arg1>HEARTBEAT</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+      <CANOpenHeartbeatPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload">
         <state>BOOTED_UP</state>
-      </CANOpenNetworkPayload>
+      </CANOpenHeartbeatPayload>
     </xml>
   </testcase>
 
@@ -81,12 +81,12 @@
     <raw>7F</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>NMT</arg1>
+      <arg1>HEARTBEAT</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+      <CANOpenHeartbeatPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload">
         <state>PRE_OPERATIONAL</state>
-      </CANOpenNetworkPayload>
+      </CANOpenHeartbeatPayload>
     </xml>
   </testcase>
 
@@ -95,21 +95,21 @@
     <raw>05</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>NMT</arg1>
+      <arg1>HEARTBEAT</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+      <CANOpenHeartbeatPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenHeartbeatPayload">
         <state>OPERATIONAL</state>
-      </CANOpenNetworkPayload>
+      </CANOpenHeartbeatPayload>
     </xml>
   </testcase>
 
   <testcase>
-    <name>SDO request, payload: 4317100000000000</name>
-    <raw>4317100000000000</raw>
+    <name>SDO request, payload: 43171000</name>
+    <raw>43171000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>SDO_REQUEST</arg1>
+      <arg1>RECEIVE_SDO</arg1>
     </parser-arguments>
     <xml>
       <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
@@ -124,11 +124,11 @@
   </testcase>
 
   <testcase>
-    <name>SDO request, payload: 00171000A00F0000</name>
-    <raw>00171000A00F0000</raw>
+    <name>SDO request, payload: 00171000<!--A00F0000--></name>
+    <raw>00171000<!--A00F0000--></raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>SDO_REQUEST</arg1>
+      <arg1>RECEIVE_SDO</arg1>
     </parser-arguments>
     <xml>
       <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
@@ -147,7 +147,7 @@
     <raw>2B171000D00F</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>SDO_REQUEST</arg1>
+      <arg1>RECEIVE_SDO</arg1>
     </parser-arguments>
     <xml>
       <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
@@ -165,11 +165,11 @@
   </testcase>
 
   <testcase>
-    <name>SDO write result, payload: 60171000D0F00000</name>
-    <raw>00171000A00F0000</raw>
+    <name>SDO write result, payload: 60171000<!--A00F0000--></name>
+    <raw>00171000<!--A00F0000--></raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>SDO_REQUEST</arg1>
+      <arg1>TRANSMIT_SDO</arg1>
     </parser-arguments>
     <xml>
       <CANOpenSDORequest className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDORequest">
@@ -185,16 +185,21 @@
 
   <!-- samples -->
   <testcase>
-    <name>SDO response, payload: 6000200000</name>
-    <raw>6000200000</raw>
+    <name>SDO response, payload: 60002000</name>
+    <raw>60002000</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>SDO_RESPONSE</arg1>
+      <arg1>TRANSMIT_SDO</arg1>
     </parser-arguments>
     <xml>
-      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
-        <state>PRE_OPERATIONAL</state>
-      </CANOpenNetworkPayload>
+      <CANOpenSDOResponse className="org.apache.plc4x.java.canopen.readwrite.CANOpenSDOResponse">
+        <command>SEGMENT_UPLOAD</command>
+        <expedited>false</expedited>
+        <placement>false</placement>
+        <index>8192</index>
+        <subindex>0</subindex>
+        <data/>
+      </CANOpenSDOResponse>
     </xml>
   </testcase>
 
@@ -203,14 +208,28 @@
     <raw>78563412</raw>
     <root-type>CANOpenPayload</root-type>
     <parser-arguments>
-      <arg1>SDO_RESPONSE</arg1>
+      <arg1>RECEIVE_SDO</arg1>
     </parser-arguments>
     <xml>
       <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
         <state>PRE_OPERATIONAL</state>
       </CANOpenNetworkPayload>
     </xml>
+  </testcase>
+
 
+  <testcase>
+    <name>PDO, payload: F9013E014C015C01</name>
+    <raw>F9013E014C015C01</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>TRANSMIT_PDO_1</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <state>PRE_OPERATIONAL</state>
+      </CANOpenNetworkPayload>
+    </xml>
   </testcase>
 
 </test:testsuite>
\ No newline at end of file


[plc4x] 11/21: Fix socketcan mspec errors.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit d936e28f4693792fb097d142879d64c4a5f385a0
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Tue Sep 1 17:58:00 2020 +0200

    Fix socketcan mspec errors.
---
 protocols/can/src/main/resources/protocols/can/socketcan.mspec | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/socketcan.mspec b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
index e657a9d..201c0d6 100644
--- a/protocols/can/src/main/resources/protocols/can/socketcan.mspec
+++ b/protocols/can/src/main/resources/protocols/can/socketcan.mspec
@@ -28,7 +28,7 @@
         ]
 
         ['false' StandardSocketCANFrame
-            [const  uint 18 '0x0']
+            [const  uint 18 'spacing' '0x0']
             [simple uint 11 'identifier']
         ]
     ]
@@ -58,7 +58,7 @@
     [reserved uint 8 '0x0'] // padding 1
     [reserved uint 8 '0x0'] // padding 2
     [array int 8 'data' COUNT 'size']
-    [padding  uint 8 'pad' '0x00' '8 - (COUNT(data))']
+    [padding  uint 8 '0x00' '8 - (COUNT(data))']
 ]
 
 [type 'SocketCAN20AFrame'


[plc4x] 12/21: More tests of canopen payload.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 015724e662d1a1a76131e5e2fd6e7d811d33caee
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Tue Sep 1 18:13:32 2020 +0200

    More tests of canopen payload.
---
 .../src/main/resources/protocols/can/canopen.mspec | 67 +++++++++++++++++-----
 .../testsuite/CANOpenPayloadTestSuite.xml          | 41 +++++++++++++
 2 files changed, 94 insertions(+), 14 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index 0c4f175..c929ae4 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -17,20 +17,21 @@
  * under the License.
  */
 
-[enum uint 4 'CANOpenService'
-    ['0b0000' BROADCAST   ]
-    ['0b0001' SYNC        ]
-    ['0b1110' NMT         ]
-    ['0b1100' SDO_REQUEST ]
-    ['0b1011' SDO_RESPONSE]
-    ['0b0011' TPDO_1]
-    ['0b0100' RPDO_1]
-    ['0b0101' TPDO_2]
-    ['0b0110' RPDO_2]
-    ['0b0111' TPDO_3]
-    ['0b1000' RPDO_3]
-    ['0b1001' TPDO_4]
-    ['0b1010' RPDO_4]
+[enum uint 4 'CANOpenService' [bit 'sdo', bit 'pdo', bit 'transmit', bit 'receive']
+    ['0b0000' BROADCAST    ['false', 'false', 'false', 'false'] ]
+    ['0b0001' SYNC         ['false', 'false', 'false', 'false'] ]
+    ['0b0010' TIME         ['false', 'false', 'false', 'false'] ]
+    ['0b1110' NMT          ['false', 'false', 'false', 'false'] ]
+    ['0b1100' SDO_REQUEST  ['true',  'false', 'false', 'true' ] ]
+    ['0b1011' SDO_RESPONSE ['true',  'false', 'true',  'false'] ]
+    ['0b0011' TPDO_1       ['false', 'true',  'true',  'false'] ]
+    ['0b0100' RPDO_1       ['false', 'true',  'false', 'true' ] ]
+    ['0b0101' TPDO_2       ['false', 'true',  'true',  'false'] ]
+    ['0b0110' RPDO_2       ['false', 'true',  'false', 'true' ] ]
+    ['0b0111' TPDO_3       ['false', 'true',  'true',  'false'] ]
+    ['0b1000' RPDO_3       ['false', 'true',  'false', 'true' ] ]
+    ['0b1001' TPDO_4       ['false', 'true',  'true',  'false'] ]
+    ['0b1010' RPDO_4       ['false', 'true',  'false', 'true' ] ]
 ]
 
 [enum uint 8 'NMTStateRequest'
@@ -55,6 +56,9 @@
             [reserved uint 1 '0x00']
             [simple uint 7 'node']
         ]
+        ['CANOpenService.TIME' CANOpenTimeSynchronization
+            [simple TimeOfDay 'timeOfDay']
+        ]
         ['CANOpenService.NMT' CANOpenNetworkPayload
             [enum NMTState 'state']
         ]
@@ -78,6 +82,30 @@
             [simple uint 8 'subindex']
             [array uint 8 'data' COUNT 'size']
         ]
+        ['CANOpenService.RPDO_1' CANOpenRPDO
+            [simple CANOpenPDO 'pdo' ['1', 'true']]
+        ]
+        ['CANOpenService.TPDO_1' CANOpenTPDO
+            [simple CANOpenPDO 'pdo' ['1', 'false']]
+        ]
+        ['CANOpenService.RPDO_2' CANOpenRPDO
+            [simple CANOpenPDO 'pdo' ['2', 'true']]
+        ]
+        ['CANOpenService.TPDO_2' CANOpenTPDO
+            [simple CANOpenPDO 'pdo' ['1', 'false']]
+        ]
+        ['CANOpenService.RPDO_3' CANOpenRPDO
+            [simple CANOpenPDO 'pdo' ['3', 'true']]
+        ]
+        ['CANOpenService.TPDO_3' CANOpenTPDO
+            [simple CANOpenPDO 'pdo' ['1', 'false']]
+        ]
+        ['CANOpenService.RPDO_4' CANOpenRPDO
+            [simple CANOpenPDO 'pdo' ['4', 'true']]
+        ]
+        ['CANOpenService.TPDO_4' CANOpenTPDO
+            [simple CANOpenPDO 'pdo' ['1', 'false']]
+        ]
     ]
 ]
 
@@ -90,3 +118,14 @@
     ['0x05' BLOCK_UPLOAD]
     ['0x06' BLOCK_DOWNLOAD]
 ]
+
+[type 'CANOpenPDO' [uint 2 'index', bit 'receive']
+
+]
+
+[type 'TimeOfDay'
+    // CiA 301 - section 7.1.6.5
+    [simple uint 28 'millis']
+    [reserved int 4 '0x00']
+    [simple uint 16 'days']
+]
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
index ecf224b..3188005 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
@@ -21,6 +21,47 @@
 
   <name>Tests of CANopen frames payload.</name>
 
+
+  <testcase>
+    <name>Time synchronization, payload: A017BD805034</name>
+    <!--
+    Initial test payload was A017BD835034, however it does not align with CANOpen spec or mapping on our side is wrong
+    -->
+    <raw>A017BD805034</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>TIME</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenTimeSynchronization className="org.apache.plc4x.java.canopen.readwrite.CANOpenTimeSynchronization">
+        <timeOfDay className="org.apache.plc4x.java.canopen.readwrite.TimeOfDay">
+          <millis>-663027446</millis>
+          <days>13392</days>
+        </timeOfDay>
+      </CANOpenTimeSynchronization>
+    </xml>
+  </testcase>
+
+  <testcase>
+    <name>Time synchronization, payload: 7019BE805034</name>
+    <!--
+    Initial test payload was 7019BE835034, however it does not align with CANOpen spec or mapping on our side is wrong
+    -->
+    <raw>7019BE805034</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>TIME</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenTimeSynchronization className="org.apache.plc4x.java.canopen.readwrite.CANOpenTimeSynchronization">
+        <timeOfDay className="org.apache.plc4x.java.canopen.readwrite.TimeOfDay">
+          <millis>-392494841</millis>
+          <days>13392</days>
+        </timeOfDay>
+      </CANOpenTimeSynchronization>
+    </xml>
+  </testcase>
+
   <testcase>
     <name>Network heartbeat, payload: 00</name>
     <raw>00</raw>


[plc4x] 06/21: Focus on payloads and not frames.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 9c612bc9b4075a3aba5c10fb0e47123a38fb9d25
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Fri Aug 28 00:14:16 2020 +0200

    Focus on payloads and not frames.
---
 .../src/main/resources/protocols/can/canopen.mspec |  41 +++++----
 .../apache/plc4x/java/can/CANOpenPlcDriver.java    | 102 +++++++++++++++++++++
 .../java/can/protocol/CANOpenProtocolLogic.java    |  62 +++++++++++++
 .../{CANOpenTest.java => CANOpenPayloadTest.java}  |   6 +-
 ...enTestSuite.xml => CANOpenPayloadTestSuite.xml} |  11 ++-
 5 files changed, 198 insertions(+), 24 deletions(-)

diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index 553d12a..54337b4 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -17,28 +17,35 @@
  * under the License.
  */
 
-[type 'CANOpenFrame'
-    [enum CANOpenService 'function']
-    [simple int 11 'identifier']
-    [reserved int 9 '0x0'] // filling gap used by extended frame, and extended marker which should always be 0
-    [simple bit 'remote']
-    [simple bit 'error']
-    [reserved int 5 '0x0']  // filling gap used by extended frame
-    [implicit uint 8 'size' 'COUNT(payload)']
-    [reserved uint 8 '0x0'] // in case of fd frame these are flags
-    [reserved uint 8 '0x0'] // padding 1
-    [reserved uint 8 '0x0'] // padding 2
-    [simple CANOpenPayload 'payload' ['function', 'size']]
+[enum uint 4 'CANOpenService'
+    ['0x00' BROADCAST]
+    ['0x07' NMT]
 ]
 
-[enum uint 4 'CANOpenService'
-    ['0b1110' NMT]
+[enum uint 8 'NMTStateRequest'
+    ['0x01' OPERATIONAL]
+    ['0x02' STOP]
+    ['0x80' PRE_OPERATIONAL]
+    ['0x81' RESET_NODE]
+    ['0x82' RESET_COMMUNICATION]
 ]
 
-[discriminatedType 'CANOpenPayload' [CANOpenService 'function', uint 8 'size']
+[enum uint 8 'NMTState'
+    ['0x00' BOOTED_UP]
+    ['0x04' STOPPED]
+    ['0x05' OPERATIONAL]
+    ['0x7f' PRE_OPERATIONAL]
+]
+
+[discriminatedType 'CANOpenPayload' [CANOpenService 'function']
     [typeSwitch 'function'
-        ['CANOpenService.NMT' CANOpenNetworkPayload [uint 8 'size']
-            [array int 8 'data' COUNT 'size']
+        ['CANOpenService.BROADCAST' CANOpenBroadcastPayload
+            [enum NMTStateRequest 'request']
+            [reserved uint 1 '0x0']
+            [simple uint 7 'node']
+        ]
+        ['CANOpenService.NMT' CANOpenNetworkPayload
+            [enum NMTState 'state']
         ]
     ]
 ]
\ No newline at end of file
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
new file mode 100644
index 0000000..b1c6fe6
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/CANOpenPlcDriver.java
@@ -0,0 +1,102 @@
+/*
+ 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.can;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import org.apache.plc4x.java.can.configuration.CANConfiguration;
+import org.apache.plc4x.java.can.context.CANDriverContext;
+import org.apache.plc4x.java.can.field.CANFieldHandler;
+import org.apache.plc4x.java.can.protocol.CANOpenProtocolLogic;
+import org.apache.plc4x.java.can.protocol.CANProtocolLogic;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.socketcan.readwrite.io.SocketCANFrameIO;
+import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
+import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
+import tel.schich.javacan.CanFrame;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+
+/**
+ */
+public class CANOpenPlcDriver extends GeneratedDriverBase<SocketCANFrame> {
+
+    @Override
+    public String getProtocolCode() {
+        return "can";
+    }
+
+    @Override
+    public String getProtocolName() {
+        return "Controller Area Network";
+    }
+
+    @Override
+    protected Class<? extends Configuration> getConfigurationType() {
+        return CANConfiguration.class;
+    }
+
+    @Override
+    protected String getDefaultTransport() {
+        return "javacan";
+    }
+
+    @Override
+    protected CANFieldHandler getFieldHandler() {
+        return new CANFieldHandler();
+    }
+
+    @Override
+    protected ProtocolStackConfigurer<SocketCANFrame> getStackConfigurer() {
+        return SingleProtocolStackConfigurer.builder(SocketCANFrame.class, SocketCANFrameIO.class)
+            .withProtocol(CANOpenProtocolLogic.class)
+            .withDriverContext(CANDriverContext.class)
+            .withPacketSizeEstimator(CANEstimator.class)
+            .build();
+    }
+
+    public static class CANEstimator implements ToIntFunction<ByteBuf> {
+        @Override
+        public int applyAsInt(ByteBuf byteBuf) {
+            if (byteBuf.readableBytes() >= 5) {
+
+                System.out.println(ByteBufUtil.prettyHexDump(byteBuf));
+                byte len = byteBuf.getByte(4);
+                System.out.println("Length " + (int) len);
+
+                CanFrame frame = CanFrame.create(byteBuf.nioBuffer());
+                System.out.println(frame);
+
+                return len + 8 /* overhead */;
+            }
+            return -1; //discard
+        }
+    }
+
+    public class CANCleaner implements Consumer<ByteBuf> {
+        @Override
+        public void accept(ByteBuf byteBuf) {
+            System.out.println("Discard");
+            byteBuf.readByte();
+        }
+    }
+}
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
new file mode 100644
index 0000000..e6f18ca
--- /dev/null
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -0,0 +1,62 @@
+/*
+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.can.protocol;
+
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
+import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
+import org.apache.plc4x.java.canopen.readwrite.types.CANOpenService;
+import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> {
+
+    private Logger logger = LoggerFactory.getLogger(CANOpenProtocolLogic.class);
+
+    @Override
+    public void onConnect(ConversationContext<SocketCANFrame> context) {
+        context.fireConnected();
+    }
+
+    @Override
+    protected void decode(ConversationContext<SocketCANFrame> context, SocketCANFrame msg) throws Exception {
+        logger.info("Decode CAN message {}", msg);
+
+        int identifier = msg.getIdentifier();
+        CANOpenService service = CANOpenService.valueOf((byte) (identifier >> 7));
+        if (service != null) {
+            ReadBuffer buffer = new ReadBuffer(msg.getData());
+            CANOpenPayload payload = CANOpenPayloadIO.staticParse(buffer, service);
+        }
+    }
+
+    @Override
+    public void close(ConversationContext<SocketCANFrame> context) {
+
+    }
+
+    @Override
+    public void onDisconnect(ConversationContext<SocketCANFrame> context) {
+
+    }
+
+}
diff --git a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenTest.java b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenPayloadTest.java
similarity index 84%
rename from sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenTest.java
rename to sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenPayloadTest.java
index a97a1eb..8af4f2b 100644
--- a/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenTest.java
+++ b/sandbox/test-java-can-driver/src/test/java/org/apache/plc4x/java/can/CANOpenPayloadTest.java
@@ -21,10 +21,10 @@ package org.apache.plc4x.java.can;
 
 import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
 
-public class CANOpenTest extends ParserSerializerTestsuiteRunner {
+public class CANOpenPayloadTest extends ParserSerializerTestsuiteRunner {
 
-    public CANOpenTest() {
-        super("/testsuite/CANOpenTestSuite.xml");
+    public CANOpenPayloadTest() {
+        super("/testsuite/CANOpenPayloadTestSuite.xml");
     }
 
 }
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
similarity index 85%
rename from sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenTestSuite.xml
rename to sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
index b99128d..b5676d9 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
@@ -22,11 +22,14 @@
   <name>Tests of socketcan/CANopen frames.</name>
 
   <testcase>
-    <name>Network heartbeat frame: 728#05</name>
-    <raw>280700000100000005</raw>
-    <root-type>CANOpenFrame</root-type>
+    <name>Network heartbeat payload: 0000</name>
+    <raw>0000</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>NMT</arg1>
+    </parser-arguments>
     <xml>
-      <SocketCANFrame className="org.apache.plc4x.java.canopen.readwrite.CANOpenFrame">
+      <SocketCANFrame className="org.apache.plc4x.java.canopen.readwrite.CANOpenPayload">
         <function>15</function>
         <identifier>5</identifier>
         <extended>false</extended>


[plc4x] 10/21: Update service list for canopen.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch feature/socketcan
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit d31f30dd4309fc0b7418e7cdfb1d95dcb96324d2
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Tue Sep 1 11:42:00 2020 +0200

    Update service list for canopen.
---
 .../can/src/main/resources/protocols/can/canopen.mspec     |  9 +++++++++
 .../plc4x/java/can/protocol/CANOpenProtocolLogic.java      |  4 ++++
 .../test/resources/testsuite/CANOpenPayloadTestSuite.xml   | 14 ++++++++++++++
 3 files changed, 27 insertions(+)

diff --git a/protocols/can/src/main/resources/protocols/can/canopen.mspec b/protocols/can/src/main/resources/protocols/can/canopen.mspec
index d5160c6..0c4f175 100644
--- a/protocols/can/src/main/resources/protocols/can/canopen.mspec
+++ b/protocols/can/src/main/resources/protocols/can/canopen.mspec
@@ -19,9 +19,18 @@
 
 [enum uint 4 'CANOpenService'
     ['0b0000' BROADCAST   ]
+    ['0b0001' SYNC        ]
     ['0b1110' NMT         ]
     ['0b1100' SDO_REQUEST ]
     ['0b1011' SDO_RESPONSE]
+    ['0b0011' TPDO_1]
+    ['0b0100' RPDO_1]
+    ['0b0101' TPDO_2]
+    ['0b0110' RPDO_2]
+    ['0b0111' TPDO_3]
+    ['0b1000' RPDO_3]
+    ['0b1001' TPDO_4]
+    ['0b1010' RPDO_4]
 ]
 
 [enum uint 8 'NMTStateRequest'
diff --git a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
index 9cc2fdb..0090e0e 100644
--- a/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
+++ b/sandbox/test-java-can-driver/src/main/java/org/apache/plc4x/java/can/protocol/CANOpenProtocolLogic.java
@@ -114,4 +114,8 @@ public class CANOpenProtocolLogic extends Plc4xProtocolBase<SocketCANFrame> impl
             (service.getValue() << 16 ) & 0x00ff0000;
     }
 
+    private CANOpenService serviceId(int nodeId) {
+        // form 32 bit socketcan identifier
+        return CANOpenService.valueOf((byte) (nodeId >> 7));
+    }
 }
diff --git a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
index d7c1a33..ecf224b 100644
--- a/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
+++ b/sandbox/test-java-can-driver/src/test/resources/testsuite/CANOpenPayloadTestSuite.xml
@@ -50,6 +50,20 @@
   </testcase>
 
   <testcase>
+    <name>Network heartbeat, payload: 05</name>
+    <raw>05</raw>
+    <root-type>CANOpenPayload</root-type>
+    <parser-arguments>
+      <arg1>NMT</arg1>
+    </parser-arguments>
+    <xml>
+      <CANOpenNetworkPayload className="org.apache.plc4x.java.canopen.readwrite.CANOpenNetworkPayload">
+        <state>OPERATIONAL</state>
+      </CANOpenNetworkPayload>
+    </xml>
+  </testcase>
+
+  <testcase>
     <name>SDO request, payload: 4317100000000000</name>
     <raw>4317100000000000</raw>
     <root-type>CANOpenPayload</root-type>