Przeglądaj źródła

Merge branch 'develop'

Signed-off-by: Martin Melik-Merkumians <melik-merkumians@acin.tuwien.ac.at>
Martin Melik-Merkumians 7 lat temu
rodzic
commit
acea92ae42
83 zmienionych plików z 3879 dodań i 1381 usunięć
  1. 1 0
      .gitignore
  2. 45 52
      .travis.yml
  3. 53 38
      README.md
  4. 4 0
      sonar-project.properties
  5. 2 0
      source/.gitignore
  6. 18 2
      source/CMakeLists.txt
  7. 303 0
      source/buildsupport/CodeCoverage.cmake
  8. 6 0
      source/buildsupport/MINGW/OpENer_PLATFORM_INCLUDES.cmake
  9. 2 2
      source/buildsupport/OpENer_Tests.cmake
  10. 1 1
      source/buildsupport/POSIX/OpENer_PLATFORM_INCLUDES.cmake
  11. 1 1
      source/src/CMakeLists.txt
  12. 46 44
      source/src/cip/appcontype.c
  13. 1 1
      source/src/cip/appcontype.h
  14. 68 51
      source/src/cip/cipassembly.c
  15. 0 3
      source/src/cip/cipclass3connection.c
  16. 135 132
      source/src/cip/cipcommon.c
  17. 12 4
      source/src/cip/cipcommon.h
  18. 13 10
      source/src/cip/cipconnectionmanager.c
  19. 10 11
      source/src/cip/cipconnectionmanager.h
  20. 78 32
      source/src/cip/cipconnectionobject.c
  21. 11 2
      source/src/cip/cipconnectionobject.h
  22. 4 5
      source/src/cip/cipelectronickey.h
  23. 81 79
      source/src/cip/cipepath.c
  24. 1 1
      source/src/cip/cipepath.h
  25. 4 3
      source/src/cip/cipethernetlink.c
  26. 14 6
      source/src/cip/cipidentity.c
  27. 4 1
      source/src/cip/cipidentity.h
  28. 76 43
      source/src/cip/cipioconnection.c
  29. 8 8
      source/src/cip/cipmessagerouter.c
  30. 8 6
      source/src/cip/cipmessagerouter.h
  31. 13 16
      source/src/cip/cipqos.c
  32. 2 3
      source/src/cip/ciptcpipinterface.c
  33. 11 12
      source/src/cip/ciptypes.h
  34. 2 0
      source/src/enet_encap/CMakeLists.txt
  35. 290 245
      source/src/enet_encap/cpf.c
  36. 31 27
      source/src/enet_encap/cpf.h
  37. 410 277
      source/src/enet_encap/encap.c
  38. 38 2
      source/src/enet_encap/encap.h
  39. 3 2
      source/src/enet_encap/endianconv.c
  40. 3 3
      source/src/enet_encap/endianconv.h
  41. 61 57
      source/src/opener_api.h
  42. 1 1
      source/src/ports/CMakeLists.txt
  43. 21 0
      source/src/ports/MINGW/CMakeLists.txt
  44. 93 0
      source/src/ports/MINGW/main.c
  45. 257 0
      source/src/ports/MINGW/networkconfig.c
  46. 0 0
      source/src/ports/MINGW/networkconfig.h
  47. 53 0
      source/src/ports/MINGW/networkhandler.c
  48. 43 0
      source/src/ports/MINGW/networkhandler.h
  49. 36 0
      source/src/ports/MINGW/opener_error.c
  50. 14 0
      source/src/ports/MINGW/sample_application/CMakeLists.txt
  51. 153 0
      source/src/ports/MINGW/sample_application/opener_user_conf.h
  52. 149 0
      source/src/ports/MINGW/sample_application/sampleapplication.c
  53. 9 1
      source/src/ports/POSIX/CMakeLists.txt
  54. 116 12
      source/src/ports/POSIX/main.c
  55. 120 78
      source/src/ports/POSIX/networkconfig.c
  56. 2 4
      source/src/ports/POSIX/networkhandler.c
  57. 1 1
      source/src/ports/POSIX/networkhandler.h
  58. 1 1
      source/src/ports/POSIX/opener_error.c
  59. 3 13
      source/src/ports/POSIX/sample_application/opener_user_conf.h
  60. 13 0
      source/src/ports/POSIX/sample_application/sampleapplication.c
  61. 2 3
      source/src/ports/WIN32/networkhandler.c
  62. 1 1
      source/src/ports/WIN32/opener_error.c
  63. 9 14
      source/src/ports/WIN32/sample_application/opener_user_conf.h
  64. 106 46
      source/src/ports/generic_networkhandler.c
  65. 1 1
      source/src/ports/generic_networkhandler.h
  66. 1 1
      source/src/ports/socket_timer.h
  67. 100 0
      source/src/ports/udp_protocol.c
  68. 124 0
      source/src/ports/udp_protocol.h
  69. 1 1
      source/src/utils/CMakeLists.txt
  70. 11 9
      source/src/utils/doublylinkedlist.c
  71. 1 1
      source/src/utils/doublylinkedlist.h
  72. 13 0
      source/src/utils/enipmessage.c
  73. 19 0
      source/src/utils/enipmessage.h
  74. 3 1
      source/tests/CMakeLists.txt
  75. 6 0
      source/tests/OpENerTests.cpp
  76. 2 0
      source/tests/OpENerTests.h
  77. 1 1
      source/tests/cip/CMakeLists.txt
  78. 266 0
      source/tests/cip/cipcommontests.cpp
  79. 58 8
      source/tests/cip/cipconnectionobjecttest.cpp
  80. 48 0
      source/tests/cip/cipepathtest.cpp
  81. 1 1
      source/tests/enet_encap/CMakeLists.txt
  82. 130 0
      source/tests/enet_encap/encaptest.cpp
  83. 16 0
      travis_scripts/compileGcovResults.sh

+ 1 - 0
.gitignore

@@ -10,3 +10,4 @@ source/src/cip_objects/
 *~
 CMakeCache.txt
 *.patch
+.idea/

+ 45 - 52
.travis.yml

@@ -1,67 +1,60 @@
-# This will run on Travis' 'new' container-based infrastructure
 sudo: false
 os: linux
 dist: trusty
-
 language: c
-
-# Blacklist
 branches:
   except:
-    - gh-pages
-
-# Environment variables
+  - gh-pages
 env:
   global:
-    - GH_REPO_NAME: OpENer
-    - DOXYFILE: opener.doxyfile
-    - GH_REPO_REF: github.com/EIPStackGroup/OpENer.git
-    - secure: "h1vuX5cGZd5W7f5TitD+EamJIsvG2qq8aBpO9MUGIOj3bShwTaR0S0qbcpCyltXiZ9DJklLc7kP5kB0XtX1o6vZMelQsqBjiHQK5yFW0vHmFAg1sLMpVBbsAN0lMWgeGJEmyRstA1KYBixwExtc5GpcgMBvS/mnJQ10zboZNcRU="
-
-# Install dependencies
+  - GH_REPO_NAME: OpENer
+  - DOXYFILE: opener.doxyfile
+  - GH_REPO_REF: github.com/EIPStackGroup/OpENer.git
+  - secure: h1vuX5cGZd5W7f5TitD+EamJIsvG2qq8aBpO9MUGIOj3bShwTaR0S0qbcpCyltXiZ9DJklLc7kP5kB0XtX1o6vZMelQsqBjiHQK5yFW0vHmFAg1sLMpVBbsAN0lMWgeGJEmyRstA1KYBixwExtc5GpcgMBvS/mnJQ10zboZNcRU=
+  - secure: CN1E5LJZwa7PLJOcst1MNb1c5Nx1rM9ifrc4llevRyTRyHxJ2S15mVQdnCvMcdPFK+ranBdsl1WcIxP3BQtI4zDwj0UWjj44EHz67VTp1o3zuaCa0fExYUwbe0D8uGRP4XbX+B4+HQWneGbabOLAZcS3Gc/pUpC3WEJO2pO2BHg=
 addons:
   apt:
     packages:
-      - doxygen
-      - doxygen-doc
-      - doxygen-latex
-      - doxygen-gui
-      - graphviz
-
+    - libcap-dev
+    - lcov
+    - doxygen
+    - doxygen-doc
+    - doxygen-latex
+    - doxygen-gui
+    - graphviz
+  sonarcloud:
+    organization: eipstackgroup
+    token: $SONAR_TOKEN
 install:
-  ############################################################################
-  # All the dependencies are installed in ${TRAVIS_BUILD_DIR}/deps/
-  ############################################################################
-  - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
-  - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
-  ############################################################################
-  # Install a recent CMake (unless already installed on OS X)
-  ############################################################################
-  - |
-    if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
-      CMAKE_URL="https://cmake.org/files/v3.7/cmake-3.7.2-Linux-x86_64.tar.gz"
-      mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
-      export PATH=${DEPS_DIR}/cmake/bin:${PATH}
-    else
-      brew upgrade cmake || brew install cmake
-    fi
-    cmake --version
-
-#Prepare CppUTest from source, as package is not available
+- git fetch --unshallow --tags
+- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
+- mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
+- |
+  if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
+    CMAKE_URL="https://cmake.org/files/v3.7/cmake-3.7.2-Linux-x86_64.tar.gz"
+    mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
+    export PATH=${DEPS_DIR}/cmake/bin:${PATH}
+  else
+    brew upgrade cmake || brew install cmake
+  fi
+  cmake --version
 before_script:
-  - cd $TRAVIS_BUILD_DIR/source
-  - chmod +x $TRAVIS_BUILD_DIR/travis_scripts/installCppUTestDependency.sh
-  - $TRAVIS_BUILD_DIR/travis_scripts/installCppUTestDependency.sh
-
-# Build your code e.g. by calling make
+- cd $TRAVIS_BUILD_DIR/source
+- chmod +x $TRAVIS_BUILD_DIR/travis_scripts/installCppUTestDependency.sh
+- "$TRAVIS_BUILD_DIR/travis_scripts/installCppUTestDependency.sh"
 script:
-  - cd $TRAVIS_BUILD_DIR/bin/posix
-  - cmake -DOpENer_PLATFORM:STRING="POSIX" -DCMAKE_BUILD_TYPE:STRING="" -DOpENer_64_BIT_DATA_TYPES_ENABLED:BOOL=ON -DOpENer_TESTS:BOOL=ON -DCPPUTEST_HOME:PATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest -DCPPUTEST_LIBRARY:FILEPATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest/src/CppUTest/libCppUTest.a -DCPPUTESTEXT_LIBRARY:FILEPATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest/src/CppUTestExt/libCppUTestExt.a ../../source
-  - make
-  - make test
-
-# Generate and deploy documentation
+- cd $TRAVIS_BUILD_DIR/source
+- cmake -DOpENer_PLATFORM:STRING="POSIX" -DCMAKE_BUILD_TYPE:STRING="Debug" -DOpENer_64_BIT_DATA_TYPES_ENABLED:BOOL=ON
+  -DOpENer_TESTS:BOOL=ON -DCPPUTEST_HOME:PATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest
+  -DCPPUTEST_LIBRARY:FILEPATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest/src/CppUTest/libCppUTest.a
+  -DCPPUTESTEXT_LIBRARY:FILEPATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest/src/CppUTestExt/libCppUTestExt.a .
+- build-wrapper-linux-x86-64 --out-dir bw-output make all
+- make test
+- make OpENer_coverage
+- chmod +x $TRAVIS_BUILD_DIR/travis_scripts/compileGcovResults.sh
+- $TRAVIS_BUILD_DIR/travis_scripts/compileGcovResults.sh
+- sonar-scanner -Dproject.settings=$TRAVIS_BUILD_DIR/sonar-project.properties -Dsonar.sources=. -Dsonar.exclusions=OpENer_coverage/**,dependencies/**,CMakeFiles/** -Dsonar.cfamily.gcov.reportsPath=./gcov_results
 after_success:
-  - cd $TRAVIS_BUILD_DIR/source
-  - chmod +x $TRAVIS_BUILD_DIR/travis_scripts/generateDocumentationAndDeploy.sh
-  - $TRAVIS_BUILD_DIR/travis_scripts/generateDocumentationAndDeploy.sh
+- cd $TRAVIS_BUILD_DIR/source
+- chmod +x $TRAVIS_BUILD_DIR/travis_scripts/generateDocumentationAndDeploy.sh
+- "$TRAVIS_BUILD_DIR/travis_scripts/generateDocumentationAndDeploy.sh"

+ 53 - 38
README.md

@@ -1,12 +1,11 @@
-[![Build Status](https://travis-ci.org/EIPStackGroup/OpENer.svg?branch=master)](https://travis-ci.org/EIPStackGroup/OpENer)
-    <p><a href="https://scan.coverity.com/projects/opener">
+[![Build Status](https://travis-ci.org/EIPStackGroup/OpENer.svg?branch=master)](https://travis-ci.org/EIPStackGroup/OpENer)<a href="https://scan.coverity.com/projects/opener">
   <img alt="Coverity Scan Build Status"
        src="https://scan.coverity.com/projects/14200/badge.svg?flat=1"/>
-</a>
- </p>
-
+</a> 
+[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=OpENer&metric=alert_status)](https://sonarcloud.io/dashboard?id=OpENer)
 [![Stories in Ready](https://badge.waffle.io/EIPStackGroup/OpENer.svg?label=ready&title=Ready)](http://waffle.io/EIPStackGroup/OpENer)
-[![Stories in In Progress](https://badge.waffle.io/EIPStackGroup/OpENer.svg?label=in%20progress&title=In%20Progress)](http://waffle.io/EIPStackGroup/OpENer) [![Join the chat at https://gitter.im/EIPStackGroupOpENer/Lobby](https://badges.gitter.im/EIPStackGroupOpENer/Lobby.svg)](https://gitter.im/EIPStackGroupOpENer/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Stories in In Progress](https://badge.waffle.io/EIPStackGroup/OpENer.svg?label=in%20progress&title=In%20Progress)](http://waffle.io/EIPStackGroup/OpENer)
+
 
 OpENer Version 2.1.0
 ====================
@@ -45,44 +44,60 @@ are part of the development packages of Cygwin.
 If you want to run the unit tests you will also have to download CppUTest via
 https://github.com/cpputest/cpputest
 
-For the development itself we recommend the use of Eclipse with the CDT plugin 
-(http://www.eclipse.org).
+For configuring the project we recommend the use of a CMake GUI (e.g., the 
+cmake-gui package on Linux, or the Installer for Windows available at [CMake](https://cmake.org/))
 
-For configuring the project we recommend the use of a CMake GUI (e.g.,the 
-cmake-gui oackage on Linux)
-
-Compile for POSIX:
+Compile for Linux/POSIX:
 ----------------
-1. Directly in the shell:
-	1. Go into the bin/posix directory
-	2. For a standard configuration invoke setup_posix.sh, otherwise start
- cmake (GUI or shell application) and configure your project
-	3. Invoke make
-	4. For invoking opener type:
+1. Make sure all the needed tools are available (CMake, make, gcc, binutils)
+2. Change to the <OpENer main folder>/bin/posix
+3. For a standard configuration invoke ``setup_posix.sh``
+	1. Invoke the ``make`` command
+	2. Grant OpENer the right to use raw sockets via ``sudo setcap cap_net_raw+ep ./src/ports/POSIX/OpENer``
+	3. Invoking OpENer:
 
-		./src/ports/POSIX/OpENer interface
+		``./src/ports/POSIX/OpENer <interface_name>``
 
-		e.g. ./src/ports/POSIX/OpENer eth1
- 
-2. Within Eclipse
-	1. For a standard configuration invoke setup_posix.sh, otherwise start
- cmake (GUI or shell application) and configure your project
-	2. Import the project
-	3. Go to the bin/win32 folder in the make targets view
-	4. Choose all from the make targets
-	5. The resulting executable will be in the directory /bin/posix or the
-directoy you have choosen via CMake
-
-Compile for Windows XP/7/8:
----------------------------
-1. Invoke setup_windows.bat or configure via CMake
-2. Open Visual Studio solution in bin/win32
-3. For invoking opener type in command line:
+		e.g. ``./src/ports/POSIX/OpENer eth1``
 
-		OpENer interface_index
+OpENer also now has a real-time capable POSIX startup via the OpENer_RT option, which requires that the used kernel has the full preemptive RT patches applied and activated.
+If you want to use OpENer_RT, instead of step 2, the  ``sudo setcap cap_net_raw,cap_ipc_lock,cap_sys_nice+ep ./src/ports/POSIX/OpENer
+`` has to be run to grant OpENEr ``CAP_SYS_NICE``, ``CAP_IPC_LOCK``, and the ``CAP_NET_RAW`` capabilities, needed for the RT mode
 
-		e.g. OpENer 3
- 
+Compile for Windows XP/7/8 via Visual Studio:
+---------------------------------------------
+1. Invoke setup_windows.bat or configure via CMake
+2. Open Visual Studio solution OpENer.sln in bin/win32
+3. Compile OpENer by chosing ``Build All`` in Visual Studio
+4. For invoking OpENer type from the command line:
+	1. Change to <OpENer main folder>\bin\win32\src\ports\WIN32\
+	2. Depending if you chose the ``Debug`` or ``Release`` configuration in Visual Studio, your executable will either show up in the subfolder Debug or Release
+	3. Invoke OpENer via
+
+		``OpENer <interface_index>``
+
+		e.g. ``OpENer 3``
+
+In order to get the correct interface index enter the command ``route print`` in a command promt and search for the MAC address of your chosen network interface at the beginning of the output. The leftmost number is the corresponding interface index.
+		
+Compile for Windows XP/7/8 via Cygwin:
+--------------------------------------
+This should work like the procedure for Linux, with the exception, that RT mode will not work for Cygwin.
+
+Compile for MinGW on Windows 10
+-------------------------------
+1. Make sure 64 bit mingw is installed. (Test with gcc --version, should show x86_64-posix-seh-rev1)
+2. Make sure CMake is installed. (Test with cmake --version, should be version 3.xx)
+3. Change to <opener install dir>/bin/mingw
+4. Run the command `setup_mingw.bat` in a dos command line. (Not a bash shell). If tracing is desired, 
+use the following (where the cmake parameter must be enclosed in quotes) or change the ./source/CMakeList.txt file.
+    ```
+    setup_mingw.bat "-DOpENer_TRACES:BOOL=TRUE"
+    ```
+5. Run the command "make" from the same directory (./bin/mingw)
+6. The opener.exe is now found in <opener install dir>\bin\mingw\src\ports\MINGW
+7. Start it like this: "opener 192.168.250.22", where the ip address is the local computer's address on the nettwork you want to use.
+		
 Directory structure:
 --------------------
 - bin ...  The resulting binaries and make files for different ports

+ 4 - 0
sonar-project.properties

@@ -0,0 +1,4 @@
+sonar.projectKey=OpENer
+sonar.organization=eipstackgroup
+sonar.cfamily.build-wrapper-output=bw-output
+sonar.host.url=https://sonarcloud.io

+ 2 - 0
source/.gitignore

@@ -0,0 +1,2 @@
+/Debug/
+/build/

+ 18 - 2
source/CMakeLists.txt

@@ -18,7 +18,7 @@ set( OpENer_Device_Config_Device_Name "OpENer PC" CACHE STRING "Device Name")
 set( OpENer_VERSION_MAJOR 2 )
 set( OpENer_VERSION_MINOR 1 )
 
-configure_file( 
+configure_file(  
 	"${PROJECT_SOURCE_DIR}/src/ports/devicedata.h.in"
 	"${PROJECT_BINARY_DIR}/src/ports/devicedata.h"
 	)
@@ -27,10 +27,22 @@ find_path( OpENer_BUILDSUPPORT_DIR OpENer.cmake ${PROJECT_SOURCE_DIR}/buildsuppo
  
 INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/OpENer.cmake )
 
+option(OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER "Shall produced data from OpENer also include a run idle header?" FALSE)
+option(OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER "Will consumed data from OpENer also include a run idle header?" TRUE)
+
+if(OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER)
+  add_definitions(-DOPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER)
+endif()
+
+if(OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER)
+  add_definitions(-DOPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER)
+endif()
+
+
 #######################################
 # Platform switches                   #
 #######################################
-set( OpENer_KNOWN_PLATFORMS "POSIX" "WIN32" )
+set( OpENer_KNOWN_PLATFORMS "POSIX" "WIN32" "MINGW")
 
 set( OpENer_PLATFORM CACHE STRINGS "Platform OpENer will be built for" )
 set_property(CACHE OpENer_PLATFORM PROPERTY STRINGS ${OpENer_KNOWN_PLATFORMS} )
@@ -65,6 +77,10 @@ if( OpENer_TESTS )
   enable_language( CXX )
   set( CPPUTEST_HOME "" CACHE PATH "Path to CppUTest directory" )
   INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/OpENer_Tests.cmake )
+  INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/CodeCoverage.cmake )
+  set(COVERAGE_LCOV_EXCLUDES 'tests/*' 'usr/*' 'ports/POSIX/sample_application/*')
+  SETUP_TARGET_FOR_COVERAGE_LCOV(NAME ${PROJECT_NAME}_coverage EXECUTABLE OpENer_Tests DEPENDENCIES OpENer_Tests)
+  add_test_includes()
   add_subdirectory( tests )
 endif( OpENer_TESTS )
 

+ 303 - 0
source/buildsupport/CodeCoverage.cmake

@@ -0,0 +1,303 @@
+# Copyright (c) 2012 - 2017, Lars Bilke
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+#    list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+#    this list of conditions and the following disclaimer in the documentation
+#    and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors
+#    may be used to endorse or promote products derived from this software without
+#    specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# CHANGES:
+#
+# 2012-01-31, Lars Bilke
+# - Enable Code Coverage
+#
+# 2013-09-17, Joakim Söderberg
+# - Added support for Clang.
+# - Some additional usage instructions.
+#
+# 2016-02-03, Lars Bilke
+# - Refactored functions to use named parameters
+#
+# 2017-06-02, Lars Bilke
+# - Merged with modified version from github.com/ufz/ogs
+#
+#
+# USAGE:
+#
+# 1. Copy this file into your cmake modules path.
+#
+# 2. Add the following line to your CMakeLists.txt:
+#      include(CodeCoverage)
+#
+# 3. Append necessary compiler flags:
+#      APPEND_COVERAGE_COMPILER_FLAGS()
+#
+# 4. If you need to exclude additional directories from the report, specify them
+#    using the COVERAGE_LCOV_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE_LCOV.
+#    Example:
+#      set(COVERAGE_LCOV_EXCLUDES 'dir1/*' 'dir2/*')
+#
+# 5. Use the functions described below to create a custom make target which
+#    runs your test executable and produces a code coverage report.
+#
+# 6. Build a Debug build:
+#      cmake -DCMAKE_BUILD_TYPE=Debug ..
+#      make
+#      make my_coverage_target
+#
+
+include(CMakeParseArguments)
+
+# Check prereqs
+find_program( GCOV_PATH gcov )
+find_program( LCOV_PATH  NAMES lcov lcov.bat lcov.exe lcov.perl)
+find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
+find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
+find_program( SIMPLE_PYTHON_EXECUTABLE python )
+
+if(NOT GCOV_PATH)
+    message(FATAL_ERROR "gcov not found! Aborting...")
+endif() # NOT GCOV_PATH
+
+if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
+    if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
+        message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
+    endif()
+elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
+    message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
+endif()
+
+set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage"
+    CACHE INTERNAL "")
+
+set(CMAKE_CXX_FLAGS_COVERAGE
+    ${COVERAGE_COMPILER_FLAGS}
+    CACHE STRING "Flags used by the C++ compiler during coverage builds."
+    FORCE )
+set(CMAKE_C_FLAGS_COVERAGE
+    ${COVERAGE_COMPILER_FLAGS}
+    CACHE STRING "Flags used by the C compiler during coverage builds."
+    FORCE )
+set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
+    ""
+    CACHE STRING "Flags used for linking binaries during coverage builds."
+    FORCE )
+set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
+    ""
+    CACHE STRING "Flags used by the shared libraries linker during coverage builds."
+    FORCE )
+mark_as_advanced(
+    CMAKE_CXX_FLAGS_COVERAGE
+    CMAKE_C_FLAGS_COVERAGE
+    CMAKE_EXE_LINKER_FLAGS_COVERAGE
+    CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
+
+if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+    message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
+endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    link_libraries(gcov)
+else()
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
+endif()
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# SETUP_TARGET_FOR_COVERAGE_LCOV(
+#     NAME testrunner_coverage                    # New target name
+#     EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+#     DEPENDENCIES testrunner                     # Dependencies to build first
+# )
+function(SETUP_TARGET_FOR_COVERAGE_LCOV)
+
+    set(options NONE)
+    set(oneValueArgs NAME)
+    set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
+    cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(NOT LCOV_PATH)
+        message(FATAL_ERROR "lcov not found! Aborting...")
+    endif() # NOT LCOV_PATH
+
+    if(NOT GENHTML_PATH)
+        message(FATAL_ERROR "genhtml not found! Aborting...")
+    endif() # NOT GENHTML_PATH
+
+    # Setup target
+    add_custom_target(${Coverage_NAME}
+
+        # Cleanup lcov
+        COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . --zerocounters
+        # Create baseline to make sure untouched files show up in the report
+        COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
+
+        # Run tests
+        COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+
+        # Capturing lcov counters and generating report
+        COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
+        # add baseline counters
+        COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total
+        COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_LCOV_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+        COMMAND ${GENHTML_PATH} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+        COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+        DEPENDS ${Coverage_DEPENDENCIES}
+        COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
+    )
+
+    # Show where to find the lcov info report
+    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+        COMMAND ;
+        COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
+    )
+
+    # Show info where to find the report
+    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+        COMMAND ;
+        COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
+    )
+
+endfunction() # SETUP_TARGET_FOR_COVERAGE_LCOV
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# SETUP_TARGET_FOR_COVERAGE_GCOVR_XML(
+#     NAME ctest_coverage                    # New target name
+#     EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+#     DEPENDENCIES executable_target         # Dependencies to build first
+# )
+function(SETUP_TARGET_FOR_COVERAGE_GCOVR_XML)
+
+    set(options NONE)
+    set(oneValueArgs NAME)
+    set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+    cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(NOT SIMPLE_PYTHON_EXECUTABLE)
+        message(FATAL_ERROR "python not found! Aborting...")
+    endif() # NOT SIMPLE_PYTHON_EXECUTABLE
+
+    if(NOT GCOVR_PATH)
+        message(FATAL_ERROR "gcovr not found! Aborting...")
+    endif() # NOT GCOVR_PATH
+
+    # Combine excludes to several -e arguments
+    set(GCOVR_EXCLUDES "")
+    foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES})
+        list(APPEND GCOVR_EXCLUDES "-e")
+        list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
+    endforeach()
+
+    add_custom_target(${Coverage_NAME}
+        # Run tests
+        ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+
+        # Running gcovr
+        COMMAND ${GCOVR_PATH} --xml
+            -r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES}
+            --object-directory=${PROJECT_BINARY_DIR}
+            -o ${Coverage_NAME}.xml
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+        DEPENDS ${Coverage_DEPENDENCIES}
+        COMMENT "Running gcovr to produce Cobertura code coverage report."
+    )
+
+    # Show info where to find the report
+    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+        COMMAND ;
+        COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
+    )
+
+endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_XML
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML(
+#     NAME ctest_coverage                    # New target name
+#     EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+#     DEPENDENCIES executable_target         # Dependencies to build first
+# )
+function(SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML)
+
+    set(options NONE)
+    set(oneValueArgs NAME)
+    set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+    cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(NOT SIMPLE_PYTHON_EXECUTABLE)
+        message(FATAL_ERROR "python not found! Aborting...")
+    endif() # NOT SIMPLE_PYTHON_EXECUTABLE
+
+    if(NOT GCOVR_PATH)
+        message(FATAL_ERROR "gcovr not found! Aborting...")
+    endif() # NOT GCOVR_PATH
+
+    # Combine excludes to several -e arguments
+    set(GCOVR_EXCLUDES "")
+    foreach(EXCLUDE ${COVERAGE_GCOVR_EXCLUDES})
+        list(APPEND GCOVR_EXCLUDES "-e")
+        list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
+    endforeach()
+
+    add_custom_target(${Coverage_NAME}
+        # Run tests
+        ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
+
+        # Create folder
+        COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
+
+        # Running gcovr
+        COMMAND ${GCOVR_PATH} --html --html-details
+            -r ${PROJECT_SOURCE_DIR} ${GCOVR_EXCLUDES}
+            --object-directory=${PROJECT_BINARY_DIR}
+            -o ${Coverage_NAME}/index.html
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+        DEPENDS ${Coverage_DEPENDENCIES}
+        COMMENT "Running gcovr to produce HTML code coverage report."
+    )
+
+    # Show info where to find the report
+    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+        COMMAND ;
+        COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
+    )
+
+endfunction() # SETUP_TARGET_FOR_COVERAGE_GCOVR_HTML
+
+function(APPEND_COVERAGE_COMPILER_FLAGS)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+    message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
+endfunction() # APPEND_COVERAGE_COMPILER_FLAGS

+ 6 - 0
source/buildsupport/MINGW/OpENer_PLATFORM_INCLUDES.cmake

@@ -0,0 +1,6 @@
+macro(opener_platform_spec)
+  #find_path( MSINTTYPES_DIR inttypes.h ${PROJECT_SOURCE_DIR}/contrib/msinttypes)
+  include_directories(${PORTS_SRC_DIR}/${OpENer_PLATFORM} ${PORTS_SRC_DIR}/${OpENer_PLATFORM}/sample_application )
+  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRESTRICT=__restrict -DWIN32" )
+endmacro(opener_platform_spec)
+

+ 2 - 2
source/buildsupport/OpENer_Tests.cmake

@@ -2,7 +2,7 @@
 # Adds test includes                  #
 #######################################
 macro( add_test_includes )
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorNewMacros.h" )
-  set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorMallocMacros.h" )
+  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorNewMacros.h" )
+  set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage -include ${CPPUTEST_HOME}/include/CppUTest/MemoryLeakDetectorMallocMacros.h" )
   include_directories( ${CPPUTEST_HOME}/include )
 endmacro( add_test_includes )

+ 1 - 1
source/buildsupport/POSIX/OpENer_PLATFORM_INCLUDES.cmake

@@ -1,7 +1,7 @@
 macro(opener_platform_spec)
   include_directories(${PORTS_SRC_DIR}/${OpENer_PLATFORM} ${PORTS_SRC_DIR}/${OpENer_PLATFORM}/sample_application)
   set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Werror=implicit-function-declaration" )
-  add_definitions( -D_POSIX_C_SOURCE=200112L -D__USE_GNU -D__USE_XOPEN2K)
+  add_definitions( -D_POSIX_C_SOURCE=200112L -D__USE_GNU -D__USE_XOPEN2K -DOPENER_POSIX)
   get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
   if ("CXX" IN_LIST languages)
   	add_definitions(-DRESTRICT=)

+ 1 - 1
source/src/CMakeLists.txt

@@ -13,7 +13,7 @@ add_subdirectory( utils )
 opener_common_includes()
 
 #######################################
-# Add platfrom specific things        #
+# Add platform specific things        #
 #######################################
 
 opener_platform_support( "INCLUDES" )

+ 46 - 44
source/src/cip/appcontype.c

@@ -142,7 +142,8 @@ CipConnectionObject *GetIoConnectionForConnectionData(
           /* we found no connection and don't have an error so try listen only next */
           io_connection = GetListenOnlyConnection(connection_object,
                                                   extended_error);
-          if ( (NULL == io_connection) && (0 == *extended_error) ) {
+          if ( (NULL == io_connection) &&
+               (kCipErrorSuccess == *extended_error) ) {
             /* no application connection type was found that suits the given data */
             *extended_error =
               kConnectionManagerExtendedStatusCodeInconsistentApplicationPathCombo;
@@ -195,18 +196,26 @@ CipConnectionObject *GetExclusiveOwnerConnection(
            ConnectionObjectGetState(exclusive_owner) ) {
           *extended_error =
             kConnectionManagerExtendedStatusCodeErrorOwnershipConflict;
-          OPENER_TRACE_INFO("Hit an Ownership conflict in appcontype.c");
+          OPENER_TRACE_INFO("Hit an Ownership conflict in appcontype.c:198\n");
           break;
         }
         if(kConnectionObjectStateTimedOut ==
            ConnectionObjectGetState(exclusive_owner)
-           && EqualConnectionTriad(connection_object, exclusive_owner) ) {
-          exclusive_owner->connection_close_function(exclusive_owner);
+           && ConnectionObjectEqualOriginator(connection_object,
+                                              exclusive_owner) ) {
+          g_exlusive_owner_connections[i].connection_data.
+          connection_close_function(&(g_exlusive_owner_connections[i].
+                                      connection_data) );
           return &(g_exlusive_owner_connections[i].connection_data);
+        } else {
+          *extended_error =
+            kConnectionManagerExtendedStatusCodeErrorOwnershipConflict;
+          OPENER_TRACE_INFO(
+            "Hit an Ownership conflict with timed out connection");
+          break;
         }
       }
       return &(g_exlusive_owner_connections[i].connection_data);
-      break;
     }
   }
   return NULL;
@@ -237,9 +246,9 @@ CipConnectionObject *GetInputOnlyConnection(
         if (kConnectionObjectStateTimedOut
             == ConnectionObjectGetState(&(g_input_only_connections[i].
                                           connection_data[j]) )
-            && EqualConnectionTriad(connection_object,
-                                    &(g_input_only_connections[i].
-                                      connection_data[j]) ) )
+            && ConnectionObjectEqualOriginator(connection_object,
+                                               &(g_input_only_connections[i].
+                                                 connection_data[j]) ) )
         {
           g_input_only_connections[i].connection_data[j].
           connection_close_function(
@@ -268,14 +277,6 @@ CipConnectionObject *GetListenOnlyConnection(
   const CipConnectionObject *const RESTRICT connection_object,
   EipUint16 *const extended_error) {
 
-  if ( kConnectionObjectConnectionTypeMulticast
-       != ConnectionObjectGetTToOConnectionType(connection_object) ) {
-    /* a listen only connection has to be a multicast connection. */
-    *extended_error =
-      kConnectionManagerExtendedStatusCodeNonListenOnlyConnectionNotOpened;   /* maybe not the best error message however there is no suitable definition in the cip spec */
-    return NULL;
-  }
-
   for (size_t i = 0; i < OPENER_CIP_NUM_LISTEN_ONLY_CONNS; i++) {
     if (g_listen_only_connections[i].output_assembly
         == connection_object->consumed_path.instance_id) { /* we have the same output assembly */
@@ -305,9 +306,9 @@ CipConnectionObject *GetListenOnlyConnection(
         if (kConnectionObjectStateTimedOut
             == ConnectionObjectGetState(&(g_listen_only_connections[i].
                                           connection_data[j]) )
-            && EqualConnectionTriad(connection_object,
-                                    &(g_listen_only_connections[i].
-                                      connection_data[j]) ) )
+            && ConnectionObjectEqualOriginator(connection_object,
+                                               &(g_listen_only_connections[i].
+                                                 connection_data[j]) ) )
         {
           g_listen_only_connections[i].connection_data[j].
           connection_close_function(
@@ -339,20 +340,20 @@ CipConnectionObject *GetExistingProducerMulticastConnection(
   while (NULL != node) {
     CipConnectionObject *producer_multicast_connection = node->data;
     if ( true ==
-         ConnectionObjectIsTypeIOConnection(producer_multicast_connection) ) {
-      if ( (input_point
-            == producer_multicast_connection->produced_path.instance_id)
-           && ( kConnectionObjectConnectionTypeMulticast
-                == ConnectionObjectGetTToOConnectionType(
-                  producer_multicast_connection) )
-           && (kEipInvalidSocket
-               != producer_multicast_connection->socket[
-                 kUdpCommuncationDirectionProducing]) ) {
-        /* we have a connection that produces the same input assembly,
-         * is a multicast producer and manages the connection.
-         */
-        return producer_multicast_connection;
-      }
+         ConnectionObjectIsTypeIOConnection(producer_multicast_connection) &&
+         (input_point ==
+          producer_multicast_connection->produced_path.instance_id) &&
+         ( kConnectionObjectConnectionTypeMulticast ==
+           ConnectionObjectGetTToOConnectionType(producer_multicast_connection) )
+         &&
+         (kEipInvalidSocket !=
+          producer_multicast_connection->socket[
+            kUdpCommuncationDirectionProducing]) )
+    {
+      /* we have a connection that produces the same input assembly,
+       * is a multicast producer and manages the connection.
+       */
+      return producer_multicast_connection;
     }
     node = node->next;
   }
@@ -367,7 +368,8 @@ CipConnectionObject *GetNextNonControlMasterConnection(
     CipConnectionObject *next_non_control_master_connection =
       node->data;
     if ( true ==
-         ConnectionObjectIsTypeIOConnection(next_non_control_master_connection)
+         ConnectionObjectIsTypeNonLOIOConnection(
+           next_non_control_master_connection)
          && kConnectionObjectStateEstablished ==
          ConnectionObjectGetState(next_non_control_master_connection)
          && input_point ==
@@ -375,9 +377,10 @@ CipConnectionObject *GetNextNonControlMasterConnection(
          &&  kConnectionObjectConnectionTypeMulticast ==
          ConnectionObjectGetTToOConnectionType(
            next_non_control_master_connection)
-         && (kEipInvalidSocket
-             == next_non_control_master_connection->socket[
-               kUdpCommuncationDirectionProducing]) ) {
+         && (kEipInvalidSocket ==
+             next_non_control_master_connection->socket[
+               kUdpCommuncationDirectionProducing
+             ]) ) {
       /* we have a connection that produces the same input assembly,
        * is a multicast producer and does not manage the connection.
        */
@@ -389,14 +392,16 @@ CipConnectionObject *GetNextNonControlMasterConnection(
 }
 
 void CloseAllConnectionsForInputWithSameType(const EipUint32 input_point,
-                                             const ConnectionObjectConnectionType instance_type)
+                                             const ConnectionObjectInstanceType instance_type)
 {
 
+  OPENER_TRACE_INFO("Close all instance type %d only connections\n",
+                    instance_type);
   DoublyLinkedListNode *node = connection_list.first;
-
   while (NULL != node) {
     CipConnectionObject *connection = node->data;
-    if ( (instance_type == connection->instance_type)
+    node = node->next;
+    if ( (instance_type == ConnectionObjectGetInstanceType(connection) )
          && (input_point == connection->produced_path.instance_id) ) {
       CipConnectionObject *connection_to_delete = connection;
       CheckIoConnectionEvent(
@@ -406,9 +411,6 @@ void CloseAllConnectionsForInputWithSameType(const EipUint32 input_point,
 
       assert(connection_to_delete->connection_close_function != NULL);
       connection_to_delete->connection_close_function(connection_to_delete);
-      node = connection_list.first;
-    } else {
-      node = node->next;
     }
   }
 }
@@ -428,7 +430,7 @@ bool ConnectionWithSameConfigPointExists(const EipUint32 config_point) {
 
   while (NULL != node) {
     CipConnectionObject *connection = node->data;
-    OPENER_ASSERT(NULL != connection);
+    OPENER_ASSERT(NULL != connection)
     if (config_point == connection->configuration_path.instance_id) {
       return true;
     }

+ 1 - 1
source/src/cip/appcontype.h

@@ -56,7 +56,7 @@ CipConnectionObject *GetNextNonControlMasterConnection(
  * @param instance_type the connection application type
  */
 void CloseAllConnectionsForInputWithSameType(const EipUint32 input_point,
-                                             const ConnectionObjectConnectionType instance_type);
+                                             const ConnectionObjectInstanceType instance_type);
 
 /**@ brief close all open connections.
  *

+ 68 - 51
source/src/cip/cipassembly.c

@@ -18,12 +18,11 @@
  *          Objects.
  *  Currently only supports Attribute 3 (CIP_BYTE_ARRAY) of an Assembly
  */
-EipStatus SetAssemblyAttributeSingle(
-  CipInstance *const instance,
-  CipMessageRouterRequest *const message_router_request,
-  CipMessageRouterResponse *const message_router_response,
-  struct sockaddr *originator_address,
-  const int encapsulation_session);
+EipStatus SetAssemblyAttributeSingle(CipInstance *const instance,
+                                     CipMessageRouterRequest *const message_router_request,
+                                     CipMessageRouterResponse *const message_router_response,
+                                     struct sockaddr *originator_address,
+                                     const int encapsulation_session);
 
 /** @brief Constructor for the assembly object class
  *
@@ -42,11 +41,15 @@ CipClass *CreateAssemblyClass(void) {
                                             "assembly", /* name */
                                             2, /* Revision, according to the CIP spec currently this has to be 2 */
                                             NULL); /* # function pointer for initialization*/
-  if (NULL != assembly_class) {
-    InsertService(assembly_class, kGetAttributeSingle, &GetAttributeSingle,
+  if(NULL != assembly_class) {
+    InsertService(assembly_class,
+                  kGetAttributeSingle,
+                  &GetAttributeSingle,
                   "GetAttributeSingle");
-    InsertService(assembly_class, kSetAttributeSingle,
-                  &SetAssemblyAttributeSingle, "SetAssemblyAttributeSingle");
+    InsertService(assembly_class,
+                  kSetAttributeSingle,
+                  &SetAssemblyAttributeSingle,
+                  "SetAssemblyAttributeSingle");
   }
 
   return assembly_class;
@@ -62,11 +65,11 @@ EipStatus CipAssemblyInitialize(void) {
 void ShutdownAssemblies(void) {
   CipClass *assembly_class = GetCipClass(kCipAssemblyClassCode);
 
-  if (NULL != assembly_class) {
+  if(NULL != assembly_class) {
     CipInstance *instance = assembly_class->instances;
-    while (NULL != instance) {
+    while(NULL != instance) {
       CipAttributeStruct *attribute = GetCipAttribute(instance, 3);
-      if (NULL != attribute) {
+      if(NULL != attribute) {
         CipFree(attribute->data);
       }
       instance = instance->next;
@@ -77,27 +80,36 @@ void ShutdownAssemblies(void) {
 CipInstance *CreateAssemblyObject(const EipUint32 instance_id,
                                   EipByte *const data,
                                   const EipUint16 data_length) {
-  CipClass *assembly_class = NULL;
-  if ( NULL == ( assembly_class = GetCipClass(kCipAssemblyClassCode) ) ) {
-    if ( NULL == ( assembly_class = CreateAssemblyClass() ) ) {
-      return NULL;
-    }
+  CipClass *assembly_class = GetCipClass(kCipAssemblyClassCode);
+  if(NULL == assembly_class) {
+    assembly_class = CreateAssemblyClass();
+  }
+
+  if(NULL == assembly_class) {
+    return NULL;
   }
 
   CipInstance *const instance = AddCIPInstance(assembly_class, instance_id);  /* add instances (always succeeds (or asserts))*/
 
-  CipByteArray *const assembly_byte_array = (CipByteArray *) CipCalloc(
-    1, sizeof(CipByteArray) );
-  if (assembly_byte_array == NULL) {
+  CipByteArray *const assembly_byte_array = (CipByteArray *) CipCalloc(1,
+                                                                       sizeof(
+                                                                         CipByteArray) );
+  if(assembly_byte_array == NULL) {
     return NULL; /*TODO remove assembly instance in case of error*/
   }
 
   assembly_byte_array->length = data_length;
   assembly_byte_array->data = data;
-  InsertAttribute(instance, 3, kCipByteArray, assembly_byte_array,
+  InsertAttribute(instance,
+                  3,
+                  kCipByteArray,
+                  assembly_byte_array,
                   kSetAndGetAble);
   /* Attribute 4 Number of bytes in Attribute 3 */
-  InsertAttribute(instance, 4, kCipUint, &(assembly_byte_array->length),
+  InsertAttribute(instance,
+                  4,
+                  kCipUint,
+                  &(assembly_byte_array->length),
                   kGetableSingle);
 
   return instance;
@@ -108,12 +120,13 @@ EipStatus NotifyAssemblyConnectedDataReceived(CipInstance *const instance,
                                               const EipUint16 data_length) {
   /* empty path (path size = 0) need to be checked and taken care of in future */
   /* copy received data to Attribute 3 */
-  CipByteArray *assembly_byte_array = (CipByteArray *) instance->attributes
-                                      ->data;
-  if (assembly_byte_array->length != data_length) {
+  CipByteArray *assembly_byte_array =
+    (CipByteArray *) instance->attributes->data;
+  if(assembly_byte_array->length != data_length) {
     OPENER_TRACE_ERR("wrong amount of data arrived for assembly object\n");
     return kEipStatusError; /*TODO question should we notify the application that wrong data has been received???*/
-  } else {
+  }
+  else{
     memcpy(assembly_byte_array->data, data, data_length);
     /* call the application that new data arrived */
   }
@@ -121,50 +134,52 @@ EipStatus NotifyAssemblyConnectedDataReceived(CipInstance *const instance,
   return AfterAssemblyDataReceived(instance);
 }
 
-EipStatus SetAssemblyAttributeSingle(
-  CipInstance *const instance,
-  CipMessageRouterRequest *const message_router_request,
-  CipMessageRouterResponse *const message_router_response,
-  struct sockaddr *originator_address,
-  const int encapsulation_session) {
+EipStatus SetAssemblyAttributeSingle(CipInstance *const instance,
+                                     CipMessageRouterRequest *const message_router_request,
+                                     CipMessageRouterResponse *const message_router_response,
+                                     struct sockaddr *originator_address,
+                                     const int encapsulation_session) {
   OPENER_TRACE_INFO(" setAttribute %d\n",
                     message_router_request->request_path.attribute_number);
 
   const EipUint8 *const router_request_data = message_router_request->data;
 
   message_router_response->data_length = 0;
-  message_router_response->reply_service = (0x80
-                                            | message_router_request->service);
+  message_router_response->reply_service =
+    (0x80 | message_router_request->service);
   message_router_response->general_status = kCipErrorAttributeNotSupported;
   message_router_response->size_of_additional_status = 0;
 
-  CipAttributeStruct *attribute = GetCipAttribute(
-    instance, message_router_request->request_path.attribute_number);
+  CipAttributeStruct *attribute = GetCipAttribute(instance,
+                                                  message_router_request->request_path.attribute_number);
 
-  if ( (attribute != NULL)
-       && (3 == message_router_request->request_path.attribute_number) ) {
-    if (attribute->data != NULL) {
+  if( (attribute != NULL) &&
+      (3 == message_router_request->request_path.attribute_number) ) {
+    if(attribute->data != NULL) {
       CipByteArray *data = (CipByteArray *) attribute->data;
 
       /* TODO: check for ATTRIBUTE_SET/GETABLE MASK */
-      if ( true == IsConnectedOutputAssembly(instance->instance_number) ) {
+      if( true == IsConnectedOutputAssembly(instance->instance_number) ) {
         OPENER_TRACE_WARN(
           "Assembly AssemblyAttributeSingle: received data for connected output assembly\n\r");
         message_router_response->general_status = kCipErrorAttributeNotSetable;
-      } else {
-        if (message_router_request->request_path_size < data->length) {
+      }
+      else{
+        if(message_router_request->request_path_size < data->length) {
           OPENER_TRACE_INFO(
             "Assembly setAssemblyAttributeSingle: not enough data received.\r\n");
           message_router_response->general_status = kCipErrorNotEnoughData;
-        } else {
-          if (message_router_request->request_path_size > data->length) {
+        }
+        else{
+          if(message_router_request->request_path_size > data->length) {
             OPENER_TRACE_INFO(
               "Assembly setAssemblyAttributeSingle: too much data received.\r\n");
             message_router_response->general_status = kCipErrorTooMuchData;
-          } else {
+          }
+          else{
             memcpy(data->data, router_request_data, data->length);
 
-            if (AfterAssemblyDataReceived(instance) != kEipStatusOk) {
+            if(AfterAssemblyDataReceived(instance) != kEipStatusOk) {
               /* punt early without updating the status... though I don't know
                * how much this helps us here, as the attribute's data has already
                * been overwritten.
@@ -175,20 +190,22 @@ EipStatus SetAssemblyAttributeSingle(
                */
               message_router_response->general_status =
                 kCipErrorInvalidAttributeValue;
-            } else {
+            }
+            else{
               message_router_response->general_status = kCipErrorSuccess;
             }
           }
         }
       }
-    } else {
+    }
+    else{
       /* the attribute was zero we are a heartbeat assembly */
       message_router_response->general_status = kCipErrorTooMuchData;
     }
   }
 
-  if ( (attribute != NULL)
-       && (4 == message_router_request->request_path.attribute_number) ) {
+  if( (attribute != NULL) &&
+      (4 == message_router_request->request_path.attribute_number) ) {
     message_router_response->general_status = kCipErrorAttributeNotSetable;
   }
 

+ 0 - 3
source/src/cip/cipclass3connection.c

@@ -28,9 +28,6 @@ EipStatus EstablishClass3Connection(
   EipUint16 *const extended_error) {
   EipStatus eip_status = kEipStatusOk;
 
-  /*TODO add check for transport type trigger */
-  /* if (0x03 == (g_stDummyConnectionObject.TransportTypeClassTrigger & 0x03)) */
-
   CipConnectionObject *explicit_connection = GetFreeExplicitConnection();
 
   if (NULL == explicit_connection) {

+ 135 - 132
source/src/cip/cipcommon.c

@@ -4,6 +4,7 @@
  *
  ******************************************************************************/
 #include <string.h>
+#include <stdio.h>
 
 #include "cipcommon.h"
 
@@ -36,25 +37,25 @@ void CipStackInit(const EipUint16 unique_connection_id) {
   EncapsulationInit();
   /* The message router is the first CIP object be initialized!!! */
   EipStatus eip_status = CipMessageRouterInit();
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
   eip_status = CipIdentityInit();
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
   eip_status = CipTcpIpInterfaceInit();
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
   eip_status = CipEthernetLinkInit();
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
   eip_status = ConnectionManagerInit(unique_connection_id);
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
   eip_status = CipAssemblyInitialize();
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
   eip_status = CipQoSInit();
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
   /* the application has to be initialized at last */
   eip_status = ApplicationInitialization();
-  OPENER_ASSERT(kEipStatusOk == eip_status);
+  OPENER_ASSERT(kEipStatusOk == eip_status)
 
   /* Shut up compiler warning with traces disabled */
-  (void) eip_status;
+    (void) eip_status;
 }
 
 void ShutdownCipStack(void) {
@@ -89,14 +90,14 @@ EipStatus NotifyClass(const CipClass *RESTRICT const cip_class,
     CipServiceStruct *service = instance->cip_class->services;             /* get pointer to array of services */
     if (NULL != service)             /* if services are defined */
     {
-      for (int i = 0; i < instance->cip_class->number_of_services; i++)                   /* seach the services list */
+      for (size_t i = 0; i < instance->cip_class->number_of_services; i++)                   /* seach the services list */
       {
         if (message_router_request->service == service->service_number)                         /* if match is found */
         {
           /* call the service, and return what it returns */
           OPENER_TRACE_INFO("notify: calling %s service\n",
                             service->name);
-          OPENER_ASSERT(NULL != service->service_function);
+          OPENER_ASSERT(NULL != service->service_function)
           return service->service_function(instance,
                                            message_router_request,
                                            message_router_response,
@@ -113,9 +114,10 @@ EipStatus NotifyClass(const CipClass *RESTRICT const cip_class,
   } else {
     OPENER_TRACE_WARN("notify: instance number %d unknown\n",
                       instance_number);
-    /* if instance not found, return an error reply*/
+    /* if instance not found, return an error reply */
     message_router_response->general_status =
-      kCipErrorPathDestinationUnknown;                           /*according to the test tool this should be the correct error flag instead of CIP_ERROR_OBJECT_DOES_NOT_EXIST;*/
+      kCipErrorPathDestinationUnknown;
+    /* according to the test tool this is the correct error flag instead of CIP_ERROR_OBJECT_DOES_NOT_EXIST */
   }
 
   /* handle error replies*/
@@ -146,12 +148,12 @@ CipInstance *AddCipInstances(CipClass *RESTRICT const cip_class,
     number_of_instances, sizeof(CipInstance) );                    /* allocate a block of memory for all created instances*/
   CipInstance *first_instance = current_instance;       /* allocate a block of memory for all created instances*/
 
-  OPENER_ASSERT(NULL != current_instance);
+  OPENER_ASSERT(NULL != current_instance)
   /* fail if run out of memory */
 
   cip_class->number_of_instances += number_of_instances;       /* add the number of instances just created to the total recorded by the class */
 
-  for (int i = 0; i < number_of_instances; i++)       /* initialize all the new instances */
+  for (size_t i = 0; i < number_of_instances; i++)       /* initialize all the new instances */
   {
     *next_instance = current_instance;             /* link the previous pointer to this new node */
 
@@ -199,7 +201,7 @@ CipClass *CreateCipClass(const EipUint32 class_id,
   OPENER_TRACE_INFO("creating class '%s' with id: 0x%" PRIX32 "\n", name,
                     class_id);
 
-  OPENER_ASSERT(NULL == GetCipClass(class_id) );      /* check if an class with the ClassID already exists */
+  OPENER_ASSERT(NULL == GetCipClass(class_id) )     /* check if an class with the ClassID already exists */
   /* should never try to redefine a class*/
 
   /* a metaClass is a class that holds the class attributes and services
@@ -208,7 +210,7 @@ CipClass *CreateCipClass(const EipUint32 class_id,
      and contains a pointer to a metaclass
      CIP never explicitly addresses a metaclass*/
 
-  CipClass *const class = (CipClass *) CipCalloc(1, sizeof(CipClass) );       /* create the class object*/
+  CipClass * const class = (CipClass *) CipCalloc(1, sizeof(CipClass) );       /* create the class object*/
   CipClass *const meta_class = (CipClass *) CipCalloc(1, sizeof(CipClass) );       /* create the metaclass object*/
 
   /* initialize the class-specific fields of the Class struct*/
@@ -228,8 +230,7 @@ CipClass *CreateCipClass(const EipUint32 class_id,
   meta_class->highest_attribute_number = highest_class_attribute_number;       /* indicate which attributes are included in class getAttributeAll*/
   meta_class->number_of_services = number_of_class_services;       /* the metaclass manages the behavior of the class itself */
   meta_class->class_name = (char *) CipCalloc(1, strlen(name) + 6);       /* fabricate the name "meta<classname>"*/
-  strcpy(meta_class->class_name, "meta-");
-  strcat(meta_class->class_name, name);
+  snprintf(meta_class->class_name, strlen(name) + 6, "meta-%s", name);
 
   /* initialize the instance-specific fields of the Class struct*/
   class->class_instance.instance_number = 0;       /* the class object is instance zero of the class it describes (weird, but that's the spec)*/
@@ -309,7 +310,7 @@ void InsertAttribute(CipInstance *const instance,
   CipAttributeStruct *attribute = instance->attributes;
   CipClass *class = instance->cip_class;
 
-  OPENER_ASSERT(NULL != attribute);
+  OPENER_ASSERT(NULL != attribute)
   /* adding a attribute to a class that was not declared to have any attributes is not allowed */
   for (int i = 0; i < instance->cip_class->number_of_attributes; i++) {
     if (attribute->data == NULL) {             /* found non set attribute */
@@ -318,7 +319,7 @@ void InsertAttribute(CipInstance *const instance,
       attribute->attribute_flags = cip_flags;
       attribute->data = data;
 
-      OPENER_ASSERT(attribute_number <= class->highest_attribute_number);
+      OPENER_ASSERT(attribute_number <= class->highest_attribute_number)
 
       size_t index = CalculateIndex(attribute_number);
 
@@ -338,7 +339,7 @@ void InsertAttribute(CipInstance *const instance,
     "Tried to insert to many attributes into class: %" PRIu32 ", instance %" PRIu32 "\n",
     instance->cip_class->class_instance.instance_number,
     instance->instance_number);
-  OPENER_ASSERT(0);
+  OPENER_ASSERT(0)
   /* trying to insert too many attributes*/
 }
 
@@ -351,7 +352,7 @@ void InsertService(const CipClass *const class,
   OPENER_TRACE_INFO("%s, number of services:%d, service number:%d\n",
                     class->class_name, class->number_of_services,
                     service_number);
-  OPENER_ASSERT(service != 0);
+  OPENER_ASSERT(service != NULL)
   /* adding a service to a class that was not declared to have services is not allowed*/
   for (int i = 0; i < class->number_of_services; i++)       /* Iterate over all service slots attached to the class */
   {
@@ -365,7 +366,7 @@ void InsertService(const CipClass *const class,
     }
     ++service;
   }
-  OPENER_ASSERT(0);
+  OPENER_ASSERT(0)
   /* adding more services than were declared is a no-no*/
 }
 
@@ -435,7 +436,7 @@ EipStatus GetAttributeSingle(CipInstance *RESTRICT const instance,
         BeforeAssemblyDataSend(instance);
       }
 
-      OPENER_ASSERT(NULL != attribute);
+      OPENER_ASSERT(NULL != attribute)
       message_router_response->data_length = EncodeData(attribute->type,
                                                         attribute->data,
                                                         &message);
@@ -612,27 +613,27 @@ int EncodeData(const EipUint8 cip_type,
   return counter;
 }
 
-int DecodeData(const EipUint8 cip_type,
-               void *const data,
-               const EipUint8 **const message) {
+int DecodeData(const EipUint8 cip_data_type,
+               void *const cip_data,
+               const EipUint8 **const cip_message) {
   int number_of_decoded_bytes = -1;
 
-  switch (cip_type)
+  switch (cip_data_type)
   /* check the data type of attribute */
   {
     case (kCipBool):
     case (kCipSint):
     case (kCipUsint):
     case (kCipByte):
-      *(EipUint8 *) (data) = **message;
-      ++(*message);
+      *(EipUint8 *) (cip_data) = **cip_message;
+      ++(*cip_message);
       number_of_decoded_bytes = 1;
       break;
 
     case (kCipInt):
     case (kCipUint):
     case (kCipWord):
-      (*(EipUint16 *) (data) ) = GetIntFromMessage(message);
+      (*(EipUint16 *) (cip_data) ) = GetIntFromMessage(cip_message);
       number_of_decoded_bytes = 2;
       break;
 
@@ -640,7 +641,7 @@ int DecodeData(const EipUint8 cip_type,
     case (kCipUdint):
     case (kCipDword):
     case (kCipReal):
-      (*(EipUint32 *) (data) ) = GetDintFromMessage(message);
+      (*(EipUint32 *) (cip_data) ) = GetDintFromMessage(cip_message);
       number_of_decoded_bytes = 4;
       break;
 
@@ -648,34 +649,34 @@ int DecodeData(const EipUint8 cip_type,
     case (kCipLint):
     case (kCipUlint):
     case (kCipLword): {
-      (*(EipUint64 *) (data) ) = GetLintFromMessage(message);
+      (*(EipUint64 *) (cip_data) ) = GetLintFromMessage(cip_message);
       number_of_decoded_bytes = 8;
     }
     break;
 #endif
 
     case (kCipString): {
-      CipString *string = (CipString *) data;
-      string->length = GetIntFromMessage(message);
-      memcpy(string->string, *message, string->length);
-      *message += string->length;
+      CipString *string = (CipString *) cip_data;
+      string->length = GetIntFromMessage(cip_message);
+      memcpy(string->string, *cip_message, string->length);
+      *cip_message += string->length;
 
       number_of_decoded_bytes = string->length + 2;           /* we have a two byte length field */
       if (number_of_decoded_bytes & 0x01) {
         /* we have an odd byte count */
-        ++(*message);
+        ++(*cip_message);
         number_of_decoded_bytes++;
       }
     }
     break;
     case (kCipShortString): {
-      CipShortString *short_string = (CipShortString *) data;
+      CipShortString *short_string = (CipShortString *) cip_data;
 
-      short_string->length = **message;
-      ++(*message);
+      short_string->length = **cip_message;
+      ++(*cip_message);
 
-      memcpy(short_string->string, *message, short_string->length);
-      *message += short_string->length;
+      memcpy(short_string->string, *cip_message, short_string->length);
+      *cip_message += short_string->length;
 
       number_of_decoded_bytes = short_string->length + 1;
       break;
@@ -688,6 +689,19 @@ int DecodeData(const EipUint8 cip_type,
   return number_of_decoded_bytes;
 }
 
+CipServiceStruct *GetCipService(const CipInstance *const instance,
+                                CipUsint service_number) {
+  CipServiceStruct *service = instance->cip_class->services;
+  for (size_t i = 0; i < instance->cip_class->number_of_services; i++)       /* hunt for the GET_ATTRIBUTE_SINGLE service*/
+  {
+    if (service->service_number == service_number) {
+      return service;   /* found the service */
+    }
+    service++;
+  }
+  return NULL; /* didn't find the service */
+}
+
 EipStatus GetAttributeAll(CipInstance *instance,
                           CipMessageRouterRequest *message_router_request,
                           CipMessageRouterResponse *message_router_response,
@@ -696,59 +710,49 @@ EipStatus GetAttributeAll(CipInstance *instance,
 
   EipUint8 *reply = message_router_response->data;       /* pointer into the reply */
   CipAttributeStruct *attribute = instance->attributes;       /* pointer to list of attributes*/
-  CipServiceStruct *service = instance->cip_class->services;       /* pointer to list of services*/
+  CipServiceStruct *service = GetCipService(instance, kGetAttributeSingle);       /* pointer to list of services*/
 
-  if (instance->instance_number == 2) {
-    OPENER_TRACE_INFO("GetAttributeAll: instance number 2\n");
+  if(NULL == service) {
+    /* GetAttributeAll service not found */
+    /* Return kEipStatusOk if cannot find GET_ATTRIBUTE_SINGLE service*/
+    return kEipStatusOk;
   }
 
-  for (int i = 0; i < instance->cip_class->number_of_services; i++)       /* hunt for the GET_ATTRIBUTE_SINGLE service*/
-  {
-    if (service->service_number == kGetAttributeSingle)             /* found the service */
-    {
-      if (0 == instance->cip_class->number_of_attributes) {
-        message_router_response->data_length = 0;                         /*there are no attributes to be sent back*/
-        message_router_response->reply_service = (0x80
-                                                  | message_router_request->
-                                                  service);
-        message_router_response->general_status =
-          kCipErrorServiceNotSupported;
-        message_router_response->size_of_additional_status = 0;
-      } else {
-        for (int j = 0; j < instance->cip_class->number_of_attributes;
-             j++)                                    /* for each instance attribute of this class */
-        {
-          int attrNum = attribute->attribute_number;
-          if (attrNum < 32
-              && ( (instance->cip_class->get_all_bit_mask[CalculateIndex(
-                                                            attrNum)]) &
-                   (1 << (attrNum % 8) ) ) )                                                                /* only return attributes that are flagged as being part of GetAttributeALl */
-          {
-            message_router_request->request_path.attribute_number =
-              attrNum;
-            if (kEipStatusOkSend
-                != service->service_function(instance,
-                                             message_router_request,
-                                             message_router_response,
-                                             originator_address,
-                                             encapsulation_session) ) {
-              message_router_response->data = reply;
-              return kEipStatusError;
-            }
-            message_router_response->data +=
-              message_router_response->data_length;
-          }
-          attribute++;
+  if (0 == instance->cip_class->number_of_attributes) {
+    message_router_response->data_length = 0;                         /*there are no attributes to be sent back*/
+    message_router_response->reply_service =
+      (0x80 | message_router_request->service);
+    message_router_response->general_status = kCipErrorServiceNotSupported;
+    message_router_response->size_of_additional_status = 0;
+  } else {
+    for (size_t j = 0; j < instance->cip_class->number_of_attributes; j++) {
+      /* for each instance attribute of this class */
+      int attribute_number = attribute->attribute_number;
+      if (attribute_number < 32 &&
+          ( (instance->cip_class->get_all_bit_mask[CalculateIndex(
+                                                     attribute_number)])
+            &
+            (1 << (attribute_number % 8) ) ) ) {
+        /* only return attributes that are flagged as being part of GetAttributeALl */
+        message_router_request->request_path.attribute_number =
+          attribute_number;
+        if (kEipStatusOkSend !=
+            service->service_function(instance, message_router_request,
+                                      message_router_response,
+                                      originator_address,
+                                      encapsulation_session) ) {
+          message_router_response->data = reply;
+          return kEipStatusError;
         }
-        message_router_response->data_length =
-          message_router_response->data - reply;
-        message_router_response->data = reply;
+        message_router_response->data += message_router_response->data_length;
       }
-      return kEipStatusOkSend;
+      attribute++;
     }
-    service++;
+    message_router_response->data_length = message_router_response->data -
+                                           reply;
+    message_router_response->data = reply;
   }
-  return kEipStatusOk;       /* Return kEipStatusOk if cannot find GET_ATTRIBUTE_SINGLE service*/
+  return kEipStatusOkSend;
 }
 
 int EncodeEPath(CipEpath *epath,
@@ -882,7 +886,6 @@ int DecodePaddedEPath(CipEpath *epath,
       default:
         OPENER_TRACE_ERR("wrong path requested\n");
         return kEipStatusError;
-        break;
     }
   }
 
@@ -906,136 +909,136 @@ size_t CalculateIndex(EipUint16 attribute_number) {
 }
 
 size_t GetSizeOfAttribute(const CipAttributeStruct *const attribute_struct) {
+  size_t data_type_size = 0;
   switch (attribute_struct->type) {
-
     case (kCipBool):
-      return sizeof(CipBool);
+      data_type_size = sizeof(CipBool);
       break;
     case (kCipSint):
-      return sizeof(CipSint);
+      data_type_size = sizeof(CipSint);
       break;
     case (kCipInt):
-      return sizeof(CipInt);
+      data_type_size = sizeof(CipInt);
       break;
     case (kCipDint):
-      return sizeof(CipDint);
+      data_type_size = sizeof(CipDint);
       break;
     case (kCipUsint):
-      return sizeof(CipUsint);
+      data_type_size = sizeof(CipUsint);
       break;
     case (kCipUint):
-      return sizeof(CipUint);
+      data_type_size = sizeof(CipUint);
       break;
     case (kCipUdint):
-      return sizeof(CipUdint);
+      data_type_size = sizeof(CipUdint);
       break;
     case (kCipReal):
-      return sizeof(CipReal);
+      data_type_size = sizeof(CipReal);
       break;
+#ifdef OPENER_SUPPORT_64BIT_DATATYPES
     case (kCipLreal):
-      return sizeof(CipLreal);
+      data_type_size = sizeof(CipLreal);
       break;
-#ifdef OPENER_SUPPORT_64BIT_DATATYPES
     case (kCipUlint):
-      return sizeof(CipUlint);
+      data_type_size = sizeof(CipUlint);
       break;
     case (kCipLint):
-      return sizeof(CipLint);
+      data_type_size = sizeof(CipLint);
       break;
     case (kCipLword):
-      return sizeof(CipLword);
+      data_type_size = sizeof(CipLword);
       break;
     case (kCipLtime):
-      return sizeof(CipLint);
+      data_type_size = sizeof(CipLint);
       break;
 #endif /* OPENER_SUPPORT_64BIT_DATATYPES */
 
     case (kCipStime):
-      return sizeof(CipDint);
+      data_type_size = sizeof(CipDint);
       break;
     case (kCipDate):
-      return sizeof(CipUint);
+      data_type_size = sizeof(CipUint);
       break;
     case (kCipTimeOfDay):
-      return sizeof(CipUdint);
+      data_type_size = sizeof(CipUdint);
       break;
     case (kCipDateAndTime):
-      return sizeof(CipUdint) + sizeof(CipUint);
+      data_type_size = sizeof(CipUdint) + sizeof(CipUint);
       break;
     case (kCipString): {
       CipString *data = (CipString *) attribute_struct->data;
-      return sizeof(CipUint) + (data->length) * sizeof(CipOctet);
+      data_type_size = sizeof(CipUint) + (data->length) * sizeof(CipOctet);
     }
     break;
     case (kCipByte):
-      return sizeof(CipByte);
+      data_type_size = sizeof(CipByte);
       break;
     case (kCipWord):
-      return sizeof(CipWord);
+      data_type_size = sizeof(CipWord);
       break;
     case (kCipDword):
-      return sizeof(CipDword);
+      data_type_size = sizeof(CipDword);
       break;
     case (kCipString2): {
       CipString *data = (CipString *) attribute_struct->data;
-      return sizeof(CipUint) + 2 * (data->length) * sizeof(CipOctet);
+      data_type_size = sizeof(CipUint) + 2 * (data->length) * sizeof(CipOctet);
     }
     break;
     case (kCipFtime):
-      return sizeof(CipDint);
+      data_type_size = sizeof(CipDint);
       break;
     case (kCipItime):
-      return sizeof(CipInt);
+      data_type_size = sizeof(CipInt);
       break;
     case (kCipStringN): {
       CipStringN *data = (CipStringN *) attribute_struct->data;
-      return sizeof(CipUint) + sizeof(CipUint)
-             + (size_t) (data->length) * (size_t) (data->size);
+      data_type_size = sizeof(CipUint) + sizeof(CipUint)
+                       + (size_t) (data->length) * (size_t) (data->size);
     }
     break;
     case (kCipShortString): {
       CipShortString *data = (CipShortString *) attribute_struct->data;
-      return sizeof(CipUsint) + (data->length) * sizeof(CipOctet);
+      data_type_size = sizeof(CipUsint) + (data->length) * sizeof(CipOctet);
     }
     break;
     case (kCipTime):
-      return sizeof(CipDint);
+      data_type_size = sizeof(CipDint);
       break;
     case (kCipEpath): {
       CipEpath *data = (CipEpath *) attribute_struct->data;
-      return 2 * (data->path_size);
+      data_type_size = 2 * (data->path_size);
     }
     break;
     case (kCipEngUnit):
-      return sizeof(CipUint);
+      data_type_size = sizeof(CipUint);
       break;
     case (kCipUsintUsint):
-      return 2 * sizeof(CipUsint);
+      data_type_size = 2 * sizeof(CipUsint);
       break;
     case (kCipUdintUdintUdintUdintUdintString): {
       CipTcpIpNetworkInterfaceConfiguration *data =
         (CipTcpIpNetworkInterfaceConfiguration *) attribute_struct->data;
-      return 5 * sizeof(CipUdint) + sizeof(CipUint)
-             + (data->domain_name.length) * sizeof(EipByte);
+      data_type_size = 5 * sizeof(CipUdint) + sizeof(CipUint)
+                       + (data->domain_name.length) * sizeof(EipByte);
     }
     break;
     case (kCip6Usint):
-      return 6 * sizeof(CipUsint);
+      data_type_size = 6 * sizeof(CipUsint);
       break;
     case (kCipMemberList):
-      return 0;
+      data_type_size = 0;
       break;
     case (kCipByteArray): {
       CipByteArray *data = (CipByteArray *) attribute_struct->data;
-      return sizeof(CipUint) + (data->length) * sizeof(CipOctet);
+      data_type_size = sizeof(CipUint) + (data->length) * sizeof(CipOctet);
     }
     break;
     case (kInternalUint6):
-      return 6 * sizeof(CipUint);
+      data_type_size = 6 * sizeof(CipUint);
       break;
     default:
-      return 0;
+      data_type_size = 0;
       break;
-
   }
+  return data_type_size;
 }

+ 12 - 4
source/src/cip/cipcommon.h

@@ -25,9 +25,11 @@ static const EipUint16 kCipUintZero = 0; /**< Zero value for returning the UINT
 
 /** @brief Check if requested service present in class/instance and call appropriate service.
  *
- * @param class class receiving the message
+ * @param cip_class class receiving the message
  * @param message_router_request request message
  * @param message_router_response reply message
+ * @param originator_address address struct of the originator as received
+ * @param encapsulation_session associated encapsulation session of the explicit message
  * @return
  *     - kEipStatusOkSend ... success
  *     - kEipStatusOk ... no reply to send back
@@ -46,6 +48,8 @@ EipStatus NotifyClass(const CipClass *const RESTRICT cip_class,
  * @param instance pointer to instance.
  * @param message_router_request pointer to request.
  * @param message_router_response pointer to response.
+ * @param originator_address address struct of the originator as received
+ * @param encapsulation_session associated encapsulation session of the explicit message
  * @return status  >0 .. success
  *          -1 .. requested attribute not available
  */
@@ -64,6 +68,8 @@ EipStatus GetAttributeSingle(
  * @param instance pointer to object instance with data.
  * @param message_router_request pointer to MR request.
  * @param message_router_response pointer for MR response.
+ * @param originator_address address struct of the originator as received
+ * @param encapsulation_session associated encapsulation session of the explicit message
  * @return length of data stream >0 .. success
  *              0 .. no reply to send
  */
@@ -74,11 +80,13 @@ EipStatus GetAttributeAll(CipInstance *instance,
                           const int encapsulation_session);
 
 /** @brief Decodes padded EPath
- *  @param epath EPath to the receiving element
- *  @param message CIP Message to decode
+ *  @param epath EPath object to the receiving element
+ *  @param message pointer to the message to decode
  *  @return Number of decoded bytes
  */
 int DecodePaddedEPath(CipEpath *epath,
-                      const EipUint8 **data);
+                      const EipUint8 **message);
+
 size_t GetSizeOfAttribute(const CipAttributeStruct *const attribute_struct);
+
 #endif /* OPENER_CIPCOMMON_H_ */

+ 13 - 10
source/src/cip/cipconnectionmanager.c

@@ -129,9 +129,9 @@ EipStatus CheckElectronicKeyData(
  * @param connection_object pointer to the connection object structure for which the connection should
  *                      be established
  * @param message_router_request pointer to the received request structure. The position of the data stream pointer has to be at the connection length entry
- * @param extended_status the extended error code in case an error happened
+ * @param extended_error the extended error code in case an error happened
  * @return general status on the establishment
- *    - EIP_OK ... on success
+ *    - kEipStatusOk ... on success
  *    - On an error the general status code to be put into the response
  */
 EipUint8 ParseConnectionPath(
@@ -241,7 +241,7 @@ EipStatus ConnectionManagerInit(EipUint16 unique_connection_id) {
 }
 
 EipStatus HandleReceivedConnectedData(
-  EipUint8 *data,
+  const EipUint8 *const data,
   int data_length,
   struct sockaddr_in *from_address
   ) {
@@ -484,6 +484,8 @@ static const HandleForwardOpenRequestFunction
  *  @param instance	pointer to CIP object instance
  *  @param message_router_request		pointer to Message Router Request.
  *  @param message_router_response		pointer to Message Router Response.
+ *  @param originator_address address struct of the originator as received
+ *  @param encapsulation_session associated encapsulation session of the explicit message
  *      @return >0 .. success, 0 .. no reply to send back
  *              -1 .. error
  */
@@ -539,7 +541,7 @@ EipStatus ForwardOpen(
   }
 
   /* Check if we have a matching or non matching request */
-  if ( ( NULL != CheckForExistingConnection(&g_dummy_connection_object) ) ) {
+  if ( NULL != CheckForExistingConnection(&g_dummy_connection_object) ) {
     OPENER_TRACE_INFO("We have a Matching request\n");
     is_matching_request = true;
 
@@ -598,7 +600,7 @@ EipStatus ForwardClose(
            && (connection_object->originator_serial_number
                == originator_serial_number) ) {
         /* found the corresponding connection object -> close it */
-        OPENER_ASSERT(NULL != connection_object->connection_close_function);
+        OPENER_ASSERT(NULL != connection_object->connection_close_function)
         if ( ( (struct sockaddr_in *) originator_address )->sin_addr.s_addr
              == connection_object->originator_address.sin_addr.s_addr ) {
           connection_object->connection_close_function(connection_object);
@@ -666,7 +668,7 @@ EipStatus ManageConnections(MilliSeconds elapsed_time) {
           /* we have a timed out connection perform watchdog time out action*/
           OPENER_TRACE_INFO(">>>>>>>>>>Connection ConnNr: %u timed out\n",
                             connection_object->connection_serial_number);
-          OPENER_ASSERT(NULL != connection_object->connection_timeout_function);
+          OPENER_ASSERT(NULL != connection_object->connection_timeout_function)
           connection_object->connection_timeout_function(connection_object);
         } else {
           connection_object->inactivity_watchdog_timer -= elapsed_time;
@@ -696,7 +698,7 @@ EipStatus ManageConnections(MilliSeconds elapsed_time) {
 
           if (connection_object->transmission_trigger_timer <= elapsed_time) { /* need to send package */
             OPENER_ASSERT(
-              NULL != connection_object->connection_send_data_function);
+              NULL != connection_object->connection_send_data_function)
             EipStatus eip_status = connection_object
                                    ->connection_send_data_function(
               connection_object);
@@ -935,7 +937,9 @@ CipConnectionObject *GetConnectedOutputAssembly(
   DoublyLinkedListNode *iterator = connection_list.first;
 
   while(NULL != iterator) {
-    if( (kConnectionObjectStateEstablished ==
+    if( kConnectionObjectInstanceTypeIOExclusiveOwner ==
+        ConnectionObjectGetInstanceType(iterator->data) &&
+        (kConnectionObjectStateEstablished ==
          ConnectionObjectGetState(iterator->data)
          || kConnectionObjectStateTimedOut ==
          ConnectionObjectGetState(iterator->data) ) &&
@@ -945,7 +949,6 @@ CipConnectionObject *GetConnectedOutputAssembly(
     }
     iterator = iterator->next;
   }
-
   return NULL;
 }
 
@@ -1354,6 +1357,7 @@ EipUint8 ParseConnectionPath(
                   *extended_error = connection_path_size - remaining_path; /*offset in 16Bit words where within the connection path the error happend*/
                   return kCipErrorPathSegmentError; /*status code for invalid segment type*/
                 }
+                break;
               default:
                 OPENER_TRACE_ERR("Not allowed in connection manager");
                 break;
@@ -1367,7 +1371,6 @@ EipUint8 ParseConnectionPath(
             *extended_error = connection_path_size - remaining_path; /*offset in 16Bit words where within the connection path the error happend*/
             return
               kConnectionManagerGeneralStatusPathSegmentErrorInUnconnectedSend;
-            break;
         }
       }
     }

+ 10 - 11
source/src/cip/cipconnectionmanager.h

@@ -107,8 +107,7 @@ typedef enum {
   kConnectionManagerExtendedStatusCodeNoConsumedConnectionIdFilterAvailable =
     0x0303,
   kConnectionManagerExtendedStatusCodeNotConfiguredToSendScheduledPriorityData
-    =
-      0x0304,
+    = 0x0304,
   kConnectionManagerExtendedStatusCodeScheduleSignatureMismatch = 0x0305,
   kConnectionManagerExtendedStatusCodeScheduleSignatureValidationNotPossible =
     0x0306,
@@ -138,23 +137,23 @@ typedef enum {
   kConnectionManagerExtendedStatusCodeNotConfiguredForOffSubnetMulticast =
     0x0813,
   kConnectionManagerExtendedStatusCodeInvalidProduceConsumeDataFormat = 0x0814,
-  kConnectionManagerExtendedStatusWrongCloser
+  kConnectionManagerExtendedStatusWrongCloser = 0xFFFF /* No a real extended error code, but needed for forward close */
 } ConnectionManagerExtendedStatusCode;
 
 /** @brief macros for comparing sequence numbers according to CIP spec vol
  * 2 3-4.2 for int type variables
- * @define SEQ_LEQ32(a, b) Checks if sequence number a is less or equal than b
- * @define SEQ_GEQ32(a, b) Checks if sequence number a is greater or equal than
+ * @def SEQ_LEQ32(a, b) Checks if sequence number a is less or equal than b
+ * @def SEQ_GEQ32(a, b) Checks if sequence number a is greater or equal than
  *  b
- *  @define SEQ_GT32(a, b) Checks if sequence number a is greater than b
+ *  @def SEQ_GT32(a, b) Checks if sequence number a is greater than b
  */
 #define SEQ_LEQ32(a, b) ( (int)( (a) - (b) ) <= 0 )
 #define SEQ_GEQ32(a, b) ( (int)( (a) - (b) ) >= 0 )
 #define SEQ_GT32(a, b) ( (int)( (a) - (b) ) > 0 )
 
 /** @brief similar macros for comparing 16 bit sequence numbers
- * @define SEQ_LEQ16(a, b) Checks if sequence number a is less or equal than b
- * @define SEQ_GEQ16(a, b) Checks if sequence number a is greater or equal than
+ * @def SEQ_LEQ16(a, b) Checks if sequence number a is less or equal than b
+ * @def SEQ_GEQ16(a, b) Checks if sequence number a is greater or equal than
  *  b
  */
 #define SEQ_LEQ16(a, b) ( (short)( (a) - (b) ) <= 0 )
@@ -167,16 +166,16 @@ static const int g_kCipConnectionManagerClassCode = 0x06;
 
 /** @brief Initialize the data of the connection manager object
  *
- *  @param A unique connection id
+ *  @param unique_connection_id A unique connection id
  *  @return kEipStatusOk if successful, otherwise kEipStatusError
  */
 EipStatus ConnectionManagerInit(EipUint16 unique_connection_id);
 
 /** @brief Get a connected object dependent on requested ConnectionID.
  *
- *   @param connection_id  requested @var connection_id of opened connection
+ *   @param connection_id Connection ID of the Connection Object to get
  *   @return pointer to connected Object
- *           0 .. connection not present in device
+ *           NULL .. connection not present in device
  */
 CipConnectionObject *GetConnectedObject(const EipUint32 connection_id);
 

+ 78 - 32
source/src/cip/cipconnectionobject.c

@@ -174,31 +174,34 @@ void ConnectionObjectInitializeFromMessage(
 
 ConnectionObjectState ConnectionObjectGetState(
   const CipConnectionObject *const connection_object) {
+  ConnectionObjectState new_state = kConnectionObjectStateInvalid;
   switch (connection_object->state) {
     case CIP_CONNECTION_OBJECT_STATE_NON_EXISTENT:
-      return kConnectionObjectStateNonExistent;
+      new_state = kConnectionObjectStateNonExistent;
       break;
     case CIP_CONNECTION_OBJECT_STATE_CONFIGURING:
-      return kConnectionObjectStateConfiguring;
+      new_state = kConnectionObjectStateConfiguring;
       break;
     case CIP_CONNECTION_OBJECT_STATE_WAITING_FOR_CONNECTION_ID:
-      return kConnectionObjectStateWaitingForConnectionID;
+      new_state = kConnectionObjectStateWaitingForConnectionID;
       break;
     case CIP_CONNECTION_OBJECT_STATE_ESTABLISHED:
-      return kConnectionObjectStateEstablished;
+      new_state = kConnectionObjectStateEstablished;
       break;
     case CIP_CONNECTION_OBJECT_STATE_TIMEOUT:
-      return kConnectionObjectStateTimedOut;
+      new_state = kConnectionObjectStateTimedOut;
       break;
     case CIP_CONNECTION_OBJECT_STATE_DEFERRED_DELETE:
-      return kConnectionObjectStateDeferredDelete;
+      new_state = kConnectionObjectStateDeferredDelete;
       break;
     case CIP_CONNECTION_OBJECT_STATE_CLOSING:
-      return kConnectionObjectStateClosing;
+      new_state = kConnectionObjectStateClosing;
       break;
     default:
-      return kConnectionObjectStateInvalid;
+      new_state = kConnectionObjectStateInvalid;
+      break;
   }
+  return new_state;
 }
 
 void ConnectionObjectSetState(CipConnectionObject *const connection_object,
@@ -233,7 +236,8 @@ void ConnectionObjectSetState(CipConnectionObject *const connection_object,
         CIP_CONNECTION_OBJECT_STATE_CLOSING;
       break;
     default:
-      OPENER_ASSERT(false); /* Never get here */
+      OPENER_ASSERT(false) /* Never get here */
+      break;
   }
 }
 
@@ -263,24 +267,38 @@ void ConnectionObjectSetInstanceType(
 
 CipUsint ConnectionObjectGetInstanceTypeForAttribute(
   const CipConnectionObject *const connection_object) {
+  CipUsint instance_type = kConnectionObjectInstanceTypeInvalid;
   switch (connection_object->instance_type) {
     case kConnectionObjectInstanceTypeExplicitMessaging:
-      return CIP_CONNECTION_OBJECT_INSTANCE_TYPE_EXPLICIT_MESSAGING;
+      instance_type = CIP_CONNECTION_OBJECT_INSTANCE_TYPE_EXPLICIT_MESSAGING;
       break;
     case kConnectionObjectInstanceTypeIO:
     case kConnectionObjectInstanceTypeIOExclusiveOwner:
     case kConnectionObjectInstanceTypeIOInputOnly:
     case kConnectionObjectInstanceTypeIOListenOnly:
-      return CIP_CONNECTION_OBJECT_INSTANCE_TYPE_IO;
+      instance_type = CIP_CONNECTION_OBJECT_INSTANCE_TYPE_IO;
       break;
     case kConnectionObjectInstanceTypeCipBridged:
-      return CIP_CONNECTION_OBJECT_INSTANCE_TYPE_CIP_BRIDGED;
+      instance_type = CIP_CONNECTION_OBJECT_INSTANCE_TYPE_CIP_BRIDGED;
       break;
     default:
-      assert(false);
+      OPENER_ASSERT(false) /* This is a fault case */
+      instance_type = kConnectionObjectInstanceTypeInvalid;
+      break;
+  }
+  return instance_type;
+}
+
+bool ConnectionObjectIsTypeNonLOIOConnection(
+  const CipConnectionObject *const connection_object) {
+  switch(connection_object->instance_type) {
+    case kConnectionObjectInstanceTypeIO:
+    case kConnectionObjectInstanceTypeIOExclusiveOwner:
+    case kConnectionObjectInstanceTypeIOInputOnly:
+      return true;
+    default: return false;
   }
-  assert(false);         //We should never come to this point
-  return 255;
+  return false;
 }
 
 bool ConnectionObjectIsTypeIOConnection(
@@ -311,50 +329,66 @@ ConnectionObjectTransportClassTriggerProductionTrigger
 ConnectionObjectGetTransportClassTriggerProductionTrigger(
   const CipConnectionObject *const connection_object) {
   const CipByte kTransportClassTriggerProductionTriggerMask = 0x70;
+
+  ConnectionObjectTransportClassTriggerProductionTrigger production_trigger =
+    kConnectionObjectTransportClassTriggerProductionTriggerInvalid;
   switch ( (connection_object->transport_class_trigger) &
            kTransportClassTriggerProductionTriggerMask ) {
     case
       CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_CYCLIC:
-      return kConnectionObjectTransportClassTriggerProductionTriggerCyclic;
+      production_trigger =
+        kConnectionObjectTransportClassTriggerProductionTriggerCyclic;
       break;
     case
       CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_CHANGE_OF_STATE
       :
-      return
+      production_trigger =
         kConnectionObjectTransportClassTriggerProductionTriggerChangeOfState;
       break;
     case
       CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_PRODUCTION_TRIGGER_APPLICATION_OBJECT
       :
-      return
+      production_trigger =
         kConnectionObjectTransportClassTriggerProductionTriggerApplicationObject;
       break;
     default:
-      return kConnectionObjectTransportClassTriggerProductionTriggerInvalid;
+      production_trigger =
+        kConnectionObjectTransportClassTriggerProductionTriggerInvalid;
+      break;
   }
+  return production_trigger;
 }
 
 ConnectionObjectTransportClassTriggerTransportClass
 ConnectionObjectGetTransportClassTriggerTransportClass(
   const CipConnectionObject *const connection_object) {
   const CipByte kTransportClassTriggerTransportClassMask = 0x0F;
+
+  ConnectionObjectTransportClassTriggerTransportClass transport_class_trigger =
+    kConnectionObjectTransportClassTriggerTransportClassInvalid;
   switch ( (connection_object->transport_class_trigger) &
            kTransportClassTriggerTransportClassMask ) {
     case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_0:
-      return kConnectionObjectTransportClassTriggerTransportClass0;
+      transport_class_trigger =
+        kConnectionObjectTransportClassTriggerTransportClass0;
       break;
     case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_1:
-      return kConnectionObjectTransportClassTriggerTransportClass1;
+      transport_class_trigger =
+        kConnectionObjectTransportClassTriggerTransportClass1;
       break;
     case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_2:
-      return kConnectionObjectTransportClassTriggerTransportClass2;
+      transport_class_trigger =
+        kConnectionObjectTransportClassTriggerTransportClass2;
       break;
     case CIP_CONNECTION_OBJECT_TRANSPORT_CLASS_TRIGGER_TRANSPORT_CLASS_3:
-      return kConnectionObjectTransportClassTriggerTransportClass3;
+      transport_class_trigger =
+        kConnectionObjectTransportClassTriggerTransportClass3;
       break;
     default:
-      return kConnectionObjectTransportClassTriggerTransportClassInvalid;
+      transport_class_trigger =
+        kConnectionObjectTransportClassTriggerTransportClassInvalid;
   }
+  return transport_class_trigger;
 }
 
 CipUint ConnectionObjectGetProducedConnectionSize(
@@ -376,8 +410,7 @@ CipUint ConnectionObjectGetConsumedConnectionSize(
 
 void ConnectionObjectSetConsumedConnectionSize(
   CipConnectionObject *const connection_object,
-  const CipUint
-  consumed_connection_size) {
+  const CipUint consumed_connection_size) {
   connection_object->consumed_connection_size = consumed_connection_size;
 }
 
@@ -444,23 +477,27 @@ void ConnectionObjectSetCipConsumedConnectionID(
 
 ConnectionObjectWatchdogTimeoutAction ConnectionObjectGetWatchdogTimeoutAction(
   const CipConnectionObject *const connection_object) {
+  ConnectionObjectWatchdogTimeoutAction timeout_action =
+    kConnectionObjectWatchdogTimeoutActionInvalid;
   switch (connection_object->watchdog_timeout_action) {
     case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_TRANSITION_TO_TIMED_OUT:
-      return kConnectionObjectWatchdogTimeoutActionTransitionToTimedOut;
+      timeout_action =
+        kConnectionObjectWatchdogTimeoutActionTransitionToTimedOut;
       break;
     case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_DELETE:
-      return kConnectionObjectWatchdogTimeoutActionAutoDelete;
+      timeout_action = kConnectionObjectWatchdogTimeoutActionAutoDelete;
       break;
     case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_AUTO_RESET:
-      return kConnectionObjectWatchdogTimeoutActionAutoReset;
+      timeout_action = kConnectionObjectWatchdogTimeoutActionAutoReset;
       break;
     case CIP_CONNECTION_OBJECT_WATCHDOG_TIMEOUT_ACTION_DEFERRED_DELETE:
-      return kConnectionObjectWatchdogTimeoutActionDeferredDelete;
+      timeout_action = kConnectionObjectWatchdogTimeoutActionDeferredDelete;
       break;
     default:
-      return kConnectionObjectWatchdogTimeoutActionInvalid;
+      timeout_action = kConnectionObjectWatchdogTimeoutActionInvalid;
       break;
   }
+  return timeout_action;
 }
 
 void ConnectionObjectSetWatchdogTimeoutAction(
@@ -685,7 +722,9 @@ ConnectionObjectPriority ConnectionObjectGetPriority(
       kConnectionObjectPriorityScheduled; break;
     case CIP_CONNECTION_OBJECT_PRIORITY_URGENT: result =
       kConnectionObjectPriorityUrgent; break;
-    default: OPENER_ASSERT(false); //Not possible to get here!
+    default: OPENER_ASSERT(false) /* Not possible to get here! */
+      result = kConnectionObjectPriorityLow;
+      break;
   }
   return result;
 }
@@ -820,3 +859,10 @@ bool EqualConnectionTriad(const CipConnectionObject *const object1,
   }
   return false;
 }
+
+bool CipConnectionObjectOriginatorHasSameIP(
+  const CipConnectionObject *const connection_object,
+  const struct sockaddr *const originator_address) {
+  return ( (struct sockaddr_in *) originator_address )->sin_addr.s_addr ==
+         connection_object->originator_address.sin_addr.s_addr;
+}

+ 11 - 2
source/src/cip/cipconnectionobject.h

@@ -109,8 +109,8 @@ struct cip_connection_object {
   CipUint produced_connection_size; /*< Attribute 7 - Limits produced connection size - for explicit messaging 0xFFFF means no predefined limit */
   CipUint consumed_connection_size; /*< Attribute 8 - Limits produced connection size - for explicit messaging 0xFFFF means no predefined limit */
   CipUint expected_packet_rate; /*< Attribute 9 - Resolution in Milliseconds */
-  CipUint cip_produced_connection_id; /*< Attribute 10 */
-  CipUint cip_consumed_connection_id; /*< Attribute 11 */
+  CipUdint cip_produced_connection_id; /*< Attribute 10 */
+  CipUdint cip_consumed_connection_id; /*< Attribute 11 */
   CipUsint watchdog_timeout_action; /*< Attribute 12 */
   CipUint produced_connection_path_length; /*< Attribute 13 */
   CipOctet *produced_connection_path; /*< Attribute 14 */
@@ -186,6 +186,8 @@ struct cip_connection_object {
   ConnectionTimeoutFunction connection_timeout_function;
   ConnectionSendDataFunction connection_send_data_function;
   ConnectionReceiveDataFunction connection_receive_data_function;
+
+  ENIPMessage last_reply_sent;
 };
 
 DoublyLinkedListNode *CipConnectionObjectListArrayAllocator(
@@ -417,6 +419,9 @@ void ConnectionObjectResetProductionInhibitTimer(
 void ConnectionObjectGeneralConfiguration(
   CipConnectionObject *const connection_object);
 
+bool ConnectionObjectIsTypeNonLOIOConnection(
+  const CipConnectionObject *const connection_object);
+
 bool ConnectionObjectIsTypeIOConnection(
   const CipConnectionObject *const connection_object);
 
@@ -426,4 +431,8 @@ bool ConnectionObjectEqualOriginator(const CipConnectionObject *const object1,
 bool EqualConnectionTriad(const CipConnectionObject *const object1,
                           const CipConnectionObject *const object2);
 
+bool CipConnectionObjectOriginatorHasSameIP(
+  const CipConnectionObject *const connection_object,
+  const struct sockaddr *const originator_address);
+
 #endif /* SRC_CIP_CIPCONNECTIONOBJECT_H_ */

+ 4 - 5
source/src/cip/cipelectronickey.h

@@ -59,9 +59,8 @@ ElectronicKeyFormat4 *ElectronicKeyFormat4New(
 void ElectronicKeyFormat4Delete(ElectronicKeyFormat4 **electronic_key);
 
 /** @brief Sets vendor ID in the electronic key
- *
+ *  @param electronic_key The electronic key to be set - will be modified
  *	@param vendor_id The vendor ID to be set into the electronic key
- *	@param electronic_key The electronic key to be set - will be modified
  */
 void ElectronicKeyFormat4SetVendorId(ElectronicKeyFormat4 *const electronic_key,
                                      const CipUint vendor_id);
@@ -76,8 +75,8 @@ CipUint ElectronicKeyFormat4GetVendorId(
 
 /** @brief Sets the device type in the electronic key
  *
- *	@param device_type The device type which shall be inserted into the electronic key
  *	@param electronic_key A format 4 electronic key
+ *	@param device_type The device type which shall be inserted into the electronic key
  */
 void ElectronicKeyFormat4SetDeviceType(
   ElectronicKeyFormat4 *const electronic_key,
@@ -85,7 +84,7 @@ void ElectronicKeyFormat4SetDeviceType(
 
 /** @brief Gets the device type from a format 4 electronic key
  *
- *      @param The format 4 electronic key from which the device type will be extracted
+ *      @param electronic_key The format 4 electronic key from which the device type will be extracted
  *      @return The device type
  */
 CipUint ElectronicKeyFormat4GetDeviceType(
@@ -93,8 +92,8 @@ CipUint ElectronicKeyFormat4GetDeviceType(
 
 /** @brief Set product code in the electronic key
  *
- *      @param product_code The product code to be inserted
  *      @param electronic_key The electronic key to be modified
+ *      @param product_code The product code to be inserted
  */
 void ElectronicKeyFormat4SetProductCode(
   ElectronicKeyFormat4 *const electronic_key,

+ 81 - 79
source/src/cip/cipepath.c

@@ -12,6 +12,7 @@
 #include "endianconv.h"
 #include "cipelectronickey.h"
 #include "trace.h"
+#include <assert.h>
 
 const unsigned int kPortSegmentExtendedPort = 15; /**< Reserved port segment port value, indicating the use of the extended port field */
 
@@ -103,7 +104,7 @@ SegmentType GetPathSegmentType(const CipOctet *const cip_path) {
       break;
     default:
       OPENER_ASSERT(
-        "Invalid Segment type in the message! We should never come here!\n");
+        "Invalid Segment type in the message! We should never come here!\n")
       break;
   }
   return result;
@@ -113,32 +114,33 @@ void SetPathSegmentType(SegmentType segment_type,
                         unsigned char *const cip_path) {
   switch (segment_type) {
     case kSegmentTypePortSegment:
-      *cip_path |= SEGMENT_TYPE_PORT_SEGMENT;
+      *cip_path = SEGMENT_TYPE_PORT_SEGMENT;
       break;
     case kSegmentTypeLogicalSegment:
-      *cip_path |= SEGMENT_TYPE_LOGICAL_SEGMENT;
+      *cip_path = SEGMENT_TYPE_LOGICAL_SEGMENT;
       break;
     case kSegmentTypeNetworkSegment:
-      *cip_path |= SEGMENT_TYPE_NETWORK_SEGMENT;
+      *cip_path = SEGMENT_TYPE_NETWORK_SEGMENT;
       break;
     case kSegmentTypeSymbolicSegment:
-      *cip_path |= SEGMENT_TYPE_SYMBOLIC_SEGMENT;
+      *cip_path = SEGMENT_TYPE_SYMBOLIC_SEGMENT;
       break;
     case kSegmentTypeDataSegment:
-      *cip_path |= SEGMENT_TYPE_DATA_SEGMENT;
+      *cip_path = SEGMENT_TYPE_DATA_SEGMENT;
       break;
     case kSegmentTypeDataTypeConstructed:
-      *cip_path |= SEGMENT_TYPE_DATA_TYPE_CONSTRUCTED;
+      *cip_path = SEGMENT_TYPE_DATA_TYPE_CONSTRUCTED;
       break;
     case kSegmentTypeDataTypeElementary:
-      *cip_path |= SEGMENT_TYPE_DATA_TYPE_ELEMENTARTY;
+      *cip_path = SEGMENT_TYPE_DATA_TYPE_ELEMENTARTY;
       break;
     case kSegmentTypeReserved:
-      *cip_path |= SEGMENT_TYPE_SEGMENT_RESERVED;
+      *cip_path = SEGMENT_TYPE_SEGMENT_RESERVED;
       break;
     default:
       OPENER_ASSERT(
-        "Invalid Segment type chosen! We should never come here!\n");
+        "Invalid Segment type chosen! We should never come here!\n")
+      break;
   }
 }
 
@@ -157,36 +159,36 @@ unsigned int GetPathPortSegmentPortIdentifier(
   const unsigned char *const cip_path) {
   const unsigned int kPortIdentifierMask = 0x0F;
   unsigned int port_identifier = *cip_path & kPortIdentifierMask;
-//  OPENER_ASSERT(0 != port_identifier, "Use of reserved port identifier 0\n");
-  OPENER_ASSERT( kSegmentTypePortSegment == GetPathSegmentType(cip_path) );
-  OPENER_ASSERT(0 != port_identifier);
+/*  OPENER_ASSERT(0 != port_identifier, "Use of reserved port identifier 0\n"); */
+  OPENER_ASSERT( kSegmentTypePortSegment == GetPathSegmentType(cip_path) )
+  OPENER_ASSERT(0 != port_identifier)
   return port_identifier;
 }
 
 void SetPathPortSegmentPortIdentifier(const unsigned int port_identifier,
                                       unsigned char *const cip_path) {
-//  OPENER_ASSERT(
-//      port_identifier < 16,
-//      "Port identifier too large for standard port identifier field\n");
-  OPENER_ASSERT(port_identifier < 16);
-  (*cip_path) |= port_identifier;
+/* OPENER_ASSERT(
+      port_identifier < 16,
+      "Port identifier too large for standard port identifier field\n"); */
+  OPENER_ASSERT(port_identifier < 16)
+    (*cip_path) |= port_identifier;
 }
 
 unsigned int GetPathPortSegmentLinkAddressSize(
   const unsigned char *const cip_path) {
-//  OPENER_ASSERT(false == GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path),
-//                "Call to non existent extended link address size\n");
+/*  OPENER_ASSERT(false == GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path),
+                "Call to non existent extended link address size\n") */
   OPENER_ASSERT( true ==
-                 GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path) );
+                 GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path) )
   return *(cip_path + 1);
 }
 
 unsigned int GetPathPortSegmentExtendedPortNumber(
   const unsigned char *const cip_path) {
-//  OPENER_ASSERT(kPortSegmentExtendedPort == GetPathPortSegmentPortIdentifier(cip_path),
-//                "There is no extended port available!\n");
+/*  OPENER_ASSERT(kPortSegmentExtendedPort == GetPathPortSegmentPortIdentifier(cip_path),
+                "There is no extended port available!\n") */
   OPENER_ASSERT( kPortSegmentExtendedPort ==
-                 GetPathPortSegmentPortIdentifier(cip_path) );
+                 GetPathPortSegmentPortIdentifier(cip_path) )
   const unsigned int kExtendedPortSegmentPosition =
     GetPathPortSegmentExtendedLinkAddressSizeBit(cip_path) == true ? 2 : 1;
   return cip_path[kExtendedPortSegmentPosition]
@@ -210,7 +212,7 @@ void SetPathPortSegmentExtendedPortIdentifier(
 
 LogicalSegmentLogicalType GetPathLogicalSegmentLogicalType(
   const unsigned char *const cip_path) {
-  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
+  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) )
   const unsigned int kLogicalTypeMask = 0x1C;
   const unsigned int logical_type = (*cip_path) & kLogicalTypeMask;
   LogicalSegmentLogicalType result = kLogicalSegmentLogicalTypeExtendedLogical;
@@ -241,7 +243,7 @@ LogicalSegmentLogicalType GetPathLogicalSegmentLogicalType(
       break;
     default:
       OPENER_ASSERT(
-        "Logical segment/logical type: It is not possible to reach this point!\n");
+        "Logical segment/logical type: It is not possible to reach this point!\n")
       break;
   }
   return result;
@@ -249,7 +251,7 @@ LogicalSegmentLogicalType GetPathLogicalSegmentLogicalType(
 
 void SetPathLogicalSegmentLogicalType(LogicalSegmentLogicalType logical_type,
                                       CipOctet *const cip_path) {
-  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
+  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) )
   switch (logical_type) {
     case kLogicalSegmentLogicalTypeClassId:
       (*cip_path) |= LOGICAL_SEGMENT_TYPE_CLASS_ID;
@@ -284,7 +286,7 @@ void SetPathLogicalSegmentLogicalType(LogicalSegmentLogicalType logical_type,
 
 LogicalSegmentLogicalFormat GetPathLogicalSegmentLogicalFormat(
   const unsigned char *const cip_path) {
-  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
+  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) )
   const unsigned int kLogicalFormatMask = 0x03;
   const unsigned int logical_format = (*cip_path) & kLogicalFormatMask;
   LogicalSegmentLogicalFormat result = kLogicalSegmentLogicalFormatEightBit;
@@ -300,7 +302,7 @@ LogicalSegmentLogicalFormat GetPathLogicalSegmentLogicalFormat(
       break;
     default:
       OPENER_ASSERT(
-        "Logical segment/logical type: Invalid logical type detected!\n");
+        "Logical segment/logical type: Invalid logical type detected!\n")
       break;
   }
   return result;
@@ -309,7 +311,7 @@ LogicalSegmentLogicalFormat GetPathLogicalSegmentLogicalFormat(
 void SetPathLogicalSegmentLogicalFormat(LogicalSegmentLogicalFormat format,
                                         CipOctet *const cip_path) {
   OPENER_ASSERT( kSegmentTypeLogicalSegment ==
-                 GetPathSegmentType( (const CipOctet *)cip_path ) );
+                 GetPathSegmentType( (const CipOctet *)cip_path ) )
   switch (format) {
     case kLogicalSegmentLogicalFormatEightBit:
       (*cip_path) |= LOGICAL_SEGMENT_FORMAT_EIGHT_BIT;
@@ -322,7 +324,7 @@ void SetPathLogicalSegmentLogicalFormat(LogicalSegmentLogicalFormat format,
       break;
     default:
       OPENER_ASSERT(
-        "Logical segment/logical type: Invalid logical type detected!\n");
+        "Logical segment/logical type: Invalid logical type detected!\n")
       break;
   }
 }
@@ -345,7 +347,8 @@ const CipDword CipEpathGetLogicalValue(const EipUint8 **message) {
       data = GetDintFromMessage(message);
       break;
     default:
-      OPENER_ASSERT(false); //shall not happen!
+      OPENER_ASSERT(false) /* shall not happen! */
+      break;
   }
   return data;
 }
@@ -353,32 +356,32 @@ const CipDword CipEpathGetLogicalValue(const EipUint8 **message) {
 size_t CipEpathSetLogicalValue(const CipDword logical_value,
                                const LogicalSegmentLogicalFormat logical_format,
                                CipOctet **message) {
+  size_t written_bytes = 0;
   switch(logical_value) {
-    case kLogicalSegmentLogicalFormatEightBit: AddSintToMessage(logical_value,
-                                                                message);
-      return 1; break;
-    case kLogicalSegmentLogicalFormatSixteenBit: MoveMessageNOctets(1,
-                                                                    (const
-                                                                     CipOctet **)message);
-      AddIntToMessage(logical_value, message);
-      return 3; break;
-    case kLogicalSegmentLogicalFormatThirtyTwoBit: MoveMessageNOctets(1,
-                                                                      (const
-                                                                       CipOctet
-                                                                       **)message);
-      AddDintToMessage(logical_value, message);
-      return 5; break;
+    case kLogicalSegmentLogicalFormatEightBit:
+      written_bytes = AddSintToMessage(logical_value, message);
+      break;
+    case kLogicalSegmentLogicalFormatSixteenBit:
+      written_bytes = MoveMessageNOctets(1, (const CipOctet **)message); /* Needed for padding */
+      written_bytes += AddIntToMessage(logical_value, message);
+      break;
+    case kLogicalSegmentLogicalFormatThirtyTwoBit:
+      written_bytes = MoveMessageNOctets(1,(const CipOctet **)message); /* Needed for padding */
+      written_bytes += AddDintToMessage(logical_value, message);
+      break;
+    default:
+      OPENER_ASSERT(false) /* This should never happen! */
+      written_bytes = 0;
   }
-  OPENER_ASSERT(false); /* This should never happen! */
-  return 0;
+  return written_bytes;
 }
 
 LogicalSegmentExtendedLogicalType GetPathLogicalSegmentExtendedLogicalType(
   const unsigned char *const cip_path) {
-//  OPENER_ASSERT(LOGICAL_SEGMENT_TYPE_EXTENDED_kLogicalSegmentLogicalTypeExtendedLogicalMessageValue == GetPathLogicalSegmentLogicalType(cip_path),
-//                "Trying to extract non-existent extended logical type");
+/*  OPENER_ASSERT(LOGICAL_SEGMENT_TYPE_EXTENDED_kLogicalSegmentLogicalTypeExtendedLogicalMessageValue == GetPathLogicalSegmentLogicalType(cip_path),
+                "Trying to extract non-existent extended logical type") */
   OPENER_ASSERT( kLogicalSegmentLogicalTypeExtendedLogical == GetPathLogicalSegmentLogicalType(
-                   cip_path) );
+                   cip_path) )
   const unsigned int extended_logical_type = *(cip_path + 1);
   LogicalSegmentExtendedLogicalType result =
     kLogicalSegmentExtendedLogicalTypeReserved;
@@ -403,10 +406,10 @@ LogicalSegmentExtendedLogicalType GetPathLogicalSegmentExtendedLogicalType(
 LogicalSegmentSpecialTypeLogicalFormat
 GetPathLogicalSegmentSpecialTypeLogicalType(const unsigned char *const cip_path)
 {
-//  OPENER_ASSERT(kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path), "Not a logical segment!\n");
-  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) );
+/*  OPENER_ASSERT(kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path), "Not a logical segment!\n") */
+  OPENER_ASSERT( kSegmentTypeLogicalSegment == GetPathSegmentType(cip_path) )
   OPENER_ASSERT( kLogicalSegmentLogicalTypeSpecial == GetPathLogicalSegmentLogicalType(
-                   cip_path) );
+                   cip_path) )
   const unsigned int kLogicalFormatMask = 0x03;
   const unsigned int logical_format = (*cip_path) & kLogicalFormatMask;
 
@@ -417,16 +420,15 @@ GetPathLogicalSegmentSpecialTypeLogicalType(const unsigned char *const cip_path)
       result = kLogicalSegmentSpecialTypeLogicalFormatElectronicKey; break;
     default: result = kLogicalSegmentSpecialTypeLogicalFormatReserved; break;
   }
-
   return result;
 }
 
 ElectronicKeySegmentFormat GetPathLogicalSegmentElectronicKeyFormat(
   const unsigned char *const cip_path) {
-//  OPENER_ASSERT(kLogicalSegmentSpecialTypeLogicalFormatElectronicKey ==
-//      GetPathLogicalSegmentSpecialTypeLogicalType(cip_path), "Not an electronic key!\n");
+/*  OPENER_ASSERT(kLogicalSegmentSpecialTypeLogicalFormatElectronicKey ==
+      GetPathLogicalSegmentSpecialTypeLogicalType(cip_path), "Not an electronic key!\n") */
   OPENER_ASSERT( kLogicalSegmentSpecialTypeLogicalFormatElectronicKey ==
-                 GetPathLogicalSegmentSpecialTypeLogicalType(cip_path) );
+                 GetPathLogicalSegmentSpecialTypeLogicalType(cip_path) )
   ElectronicKeySegmentFormat result = kElectronicKeySegmentFormatReserved;
   switch( *(cip_path + 1) ) {
     case ELECTRONIC_KEY_SEGMENT_KEY_FORMAT_4: result =
@@ -440,7 +442,7 @@ void GetElectronicKeyFormat4FromMessage(
   const CipOctet **const message,
   ElectronicKeyFormat4 *key) {
   OPENER_ASSERT( kElectronicKeySegmentFormatKeyFormat4 ==
-                 GetPathLogicalSegmentElectronicKeyFormat(*message) );
+                 GetPathLogicalSegmentElectronicKeyFormat(*message) )
 
   MoveMessageNOctets(2, message);
   ElectronicKeyFormat4SetVendorId(key, GetIntFromMessage(message) );
@@ -463,7 +465,7 @@ void GetElectronicKeyFormat4FromMessage(
  */
 NetworkSegmentSubtype GetPathNetworkSegmentSubtype(
   const unsigned char *const cip_path) {
-  OPENER_ASSERT( kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) );
+  OPENER_ASSERT( kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) )
   const unsigned int kSubtypeMask = 0x1F;
   const unsigned int subtype = (*cip_path) & kSubtypeMask;
   NetworkSegmentSubtype result = kNetworkSegmentSubtypeReserved;
@@ -494,12 +496,12 @@ NetworkSegmentSubtype GetPathNetworkSegmentSubtype(
  */
 CipUsint GetPathNetworkSegmentProductionInhibitTimeInMilliseconds(
   const unsigned char *const cip_path) {
-//  OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path),"Not a network segment!\n");
-//  OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds == GetPathNetworkSegmentSubtype(cip_path),
-//                "Not a Production Inhibit Time milliseconds segment!\n");
-  OPENER_ASSERT( kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) );
+/*  OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path),"Not a network segment!\n")
+   OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds == GetPathNetworkSegmentSubtype(cip_path),
+                "Not a Production Inhibit Time milliseconds segment!\n") */
+  OPENER_ASSERT( kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) )
   OPENER_ASSERT( kNetworkSegmentSubtypeProductionInhibitTimeInMilliseconds == GetPathNetworkSegmentSubtype(
-                   cip_path) );
+                   cip_path) )
   return *(cip_path + 1);
 }
 
@@ -511,15 +513,15 @@ CipUsint GetPathNetworkSegmentProductionInhibitTimeInMilliseconds(
  */
 CipUdint GetPathNetworkSegmentProductionInhibitTimeInMicroseconds(
   const unsigned char *const cip_path) {
-//  OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path),"Not a network segment!\n");
-//  OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMicroseconds == GetPathNetworkSegmentSubtype(cip_path),
-//                  "Not a Production Inhibit Time microseconds segment!\n");
-//  OPENER_ASSERT(2 == *(cip_path + 1), "Data Words length is incorrect! See CIP Spec Vol.1 C-1.4.3.3.2\n");
+/*  OPENER_ASSERT(kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path),"Not a network segment!\n")
+   OPENER_ASSERT(kNetworkSegmentSubtypeProductionInhibitTimeInMicroseconds == GetPathNetworkSegmentSubtype(cip_path),
+                  "Not a Production Inhibit Time microseconds segment!\n")
+   OPENER_ASSERT(2 == *(cip_path + 1), "Data Words length is incorrect! See CIP Spec Vol.1 C-1.4.3.3.2\n") */
 
-  OPENER_ASSERT( kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) );
+  OPENER_ASSERT( kSegmentTypeNetworkSegment == GetPathSegmentType(cip_path) )
   OPENER_ASSERT( kNetworkSegmentSubtypeProductionInhibitTimeInMicroseconds == GetPathNetworkSegmentSubtype(
-                   cip_path) );
-  OPENER_ASSERT( 2 == *(cip_path + 1) );
+                   cip_path) )
+  OPENER_ASSERT( 2 == *(cip_path + 1) )
 
   const unsigned char *message_runner = cip_path + 2;
   return GetDintFromMessage(&message_runner);
@@ -543,7 +545,7 @@ unsigned int GetPathSymbolicSegmentASCIIFormatLength(
   const unsigned char *const cip_path) {
   const unsigned int kSymbolicSegmentASCIIFormatLength = 0x1F;
   const unsigned int length = *cip_path & kSymbolicSegmentASCIIFormatLength;
-  OPENER_ASSERT(0 != length);
+  OPENER_ASSERT(0 != length)
   return length;
 }
 
@@ -567,9 +569,9 @@ SymbolicSegmentExtendedFormat GetPathSymbolicSegmentNumericType(
 
 SymbolicSegmentExtendedFormat GetPathSymbolicSegmentExtendedFormat(
   const unsigned char *const cip_path) {
-  OPENER_ASSERT( kSegmentTypeSymbolicSegment == GetPathSegmentType(cip_path) );
+  OPENER_ASSERT( kSegmentTypeSymbolicSegment == GetPathSegmentType(cip_path) )
   OPENER_ASSERT( kSymbolicSegmentFormatExtendedString == GetPathSymbolicSegmentFormat(
-                   cip_path) );
+                   cip_path) )
   const unsigned int kSymbolicSegmentExtendedFormatMask = 0xE0;
   const unsigned int extended_type = *(cip_path + 1) &
                                      kSymbolicSegmentExtendedFormatMask;
@@ -613,11 +615,11 @@ DataSegmentSubtype GetPathDataSegmentSubtype(const unsigned char *const cip_path
  */
 CipUsint GetPathDataSegmentSimpleDataWordLength(
   const unsigned char *const cip_path) {
-//  OPENER_ASSERT(kSegmentTypeDataSegment == GetPathSegmentType(cip_path),"Not a data segment!\n");
-//  OPENER_ASSERT(kDataSegmentSubtypeSimpleData == GetPathDataSegmentSubtype(cip_path), "Not a simple data segment!\n");
-  OPENER_ASSERT( kSegmentTypeDataSegment == GetPathSegmentType(cip_path) );
+/*  OPENER_ASSERT(kSegmentTypeDataSegment == GetPathSegmentType(cip_path),"Not a data segment!\n");
+    OPENER_ASSERT(kDataSegmentSubtypeSimpleData == GetPathDataSegmentSubtype(cip_path), "Not a simple data segment!\n") */
+  OPENER_ASSERT( kSegmentTypeDataSegment == GetPathSegmentType(cip_path) )
   OPENER_ASSERT( kDataSegmentSubtypeSimpleData ==
-                 GetPathDataSegmentSubtype(cip_path) );
+                 GetPathDataSegmentSubtype(cip_path) )
 
   const unsigned char *message_runner = cip_path + 1;
   return GetSintFromMessage(&message_runner);

+ 1 - 1
source/src/cip/cipepath.h

@@ -291,7 +291,7 @@ ElectronicKeySegmentFormat GetPathLogicalSegmentElectronicKeyFormat(
 /** @brief Gets the data for an Electronic Key of format 4 from the EPath message
  *
  * @param cip_path The start of the EPath message
- * @param Writes the data on the user provided data electronic key struct
+ * @param key Writes the data on the user provided data electronic key struct
  */
 void GetElectronicKeyFormat4FromMessage(
   const CipOctet **const cip_path,

+ 4 - 3
source/src/cip/cipethernetlink.c

@@ -41,7 +41,7 @@ EipStatus GetAttributeSingleEthernetLink(
   struct sockaddr *originator_address,
   const int encapsulation_session);
 
-/** @bried Configures the MAC address of the Ethernet Link object*
+/** @brief Configures the MAC address of the Ethernet Link object*
  *
  *  @param mac_address The MAC address of the Ethernet Link
  */
@@ -66,7 +66,8 @@ CipEthernetLinkInterfaceCapability interface_capability = {
 };
 
 EipStatus CipEthernetLinkInit() {
-  CipClass *ethernet_link_class = CreateCipClass(CIP_ETHERNETLINK_CLASS_CODE, 0, /* # class attributes*/
+  CipClass *ethernet_link_class = CreateCipClass(CIP_ETHERNETLINK_CLASS_CODE,
+                                                 0, /* # class attributes*/
                                                  7, /* # highest class attribute number*/
                                                  2, /* # class services*/
                                                  11, /* # instance attributes*/
@@ -155,7 +156,7 @@ int EncodeInterfaceCapability(EipUint8 **pa_acMsg) {
     &interface_capability.speed_duplex_options.speed_duplex_array_count,
     pa_acMsg);
 
-  for (int i = 0;
+  for (CipUsint i = 0;
        i < interface_capability.speed_duplex_options.speed_duplex_array_count;
        i++) {
     return_value += EncodeData(

+ 14 - 6
source/src/cip/cipidentity.c

@@ -34,16 +34,17 @@
 #include "ciperror.h"
 #include "endianconv.h"
 #include "opener_api.h"
+#include "trace.h"
 
 /* attributes in CIP Identity Object */
 
-EipUint16 vendor_id_ = OPENER_DEVICE_VENDOR_ID; /**< Attribute 1: Vendor ID */
-EipUint16 device_type_ = OPENER_DEVICE_TYPE; /**< Attribute 2: Device Type */
-EipUint16 product_code_ = OPENER_DEVICE_PRODUCT_CODE; /**< Attribute 3: Product Code */
+CipUint vendor_id_ = OPENER_DEVICE_VENDOR_ID; /**< Attribute 1: Vendor ID */
+CipUint device_type_ = OPENER_DEVICE_TYPE; /**< Attribute 2: Device Type */
+CipUint product_code_ = OPENER_DEVICE_PRODUCT_CODE; /**< Attribute 3: Product Code */
 CipRevision revision_ = { OPENER_DEVICE_MAJOR_REVISION,
                           OPENER_DEVICE_MINOR_REVISION }; /**< Attribute 4: Revision / USINT Major, USINT Minor */
-EipUint16 status_ = 0; /**< Attribute 5: Status */
-EipUint32 serial_number_ = 0; /**< Attribute 6: Serial Number, has to be set prior to OpENer initialization */
+CipWord status_ = 0; /**< Attribute 5: Status */
+CipUdint serial_number_ = 0; /**< Attribute 6: Serial Number, has to be set prior to OpENer initialization */
 CipShortString product_name_ = { sizeof(OPENER_DEVICE_NAME) - 1,
                                  OPENER_DEVICE_NAME }; /**< Attribute 7: Product Name */
 
@@ -146,7 +147,8 @@ void InitializeCipIdentiy(CipClass *class) {
 
 EipStatus CipIdentityInit() {
 
-  CipClass *class = CreateCipClass(kIdentityClassCode, 0, /* # of non-default class attributes */
+  CipClass *class = CreateCipClass(kIdentityClassCode,
+                                   0, /* # of non-default class attributes */
                                    7, /* # highest class attribute number*/
                                    2, /* # of class services*/
                                    7, /* # of instance attributes*/
@@ -179,3 +181,9 @@ EipStatus CipIdentityInit() {
 
   return kEipStatusOk;
 }
+
+CipIdentitySetExtendedDeviceStatus(CipIdentityExtendedStatus extended_status) {
+  OPENER_TRACE_INFO("Setting extended status: %x", extended_status);
+  status_ &= ~(0x70);
+  status_ |= extended_status;
+}

+ 4 - 1
source/src/cip/cipidentity.h

@@ -37,7 +37,7 @@ typedef enum {
   kNoIoConnectionsEstablished = 0x0030,
   kNonVolatileConfigurationBad = 0x0040,
   kMajorFault = 0x0050,
-  kAtLeastOneIoConnectionInRuneMode = 0x0060,
+  kAtLeastOneIoConnectionInRunMode = 0x0060,
   kAtLeastOneIoConnectionEstablishedAllInIdleMode = 0x0070
 } CipIdentityExtendedStatus;
 
@@ -50,4 +50,7 @@ typedef enum {
  */
 EipStatus CipIdentityInit(void);
 
+void CipIdentitySetExtendedDeviceStatus(
+  CipIdentityExtendedStatus extended_status);
+
 #endif /* OPENER_CIPIDENTITY_H_ */

+ 76 - 43
source/src/cip/cipioconnection.c

@@ -12,6 +12,7 @@
 #include "generic_networkhandler.h"
 #include "cipconnectionmanager.h"
 #include "cipassembly.h"
+#include "cipidentity.h"
 #include "ciptcpipinterface.h"
 #include "cipcommon.h"
 #include "appcontype.h"
@@ -69,7 +70,7 @@ EipStatus HandleReceivedIoConnectionData(
 EipUint8 *g_config_data_buffer = NULL; /**< buffers for the config data coming with a forward open request. */
 unsigned int g_config_data_length = 0; /**< length of g_config_data_buffer. Initialized with 0 */
 
-EipUint32 g_run_idle_state; /**< buffer for holding the run idle information. */
+EipUint32 g_run_idle_state = 0; /**< buffer for holding the run idle information. */
 
 EipUint16 ProcessProductionInhibitTime(CipConnectionObject *io_connection_object)
 {
@@ -132,7 +133,7 @@ EipUint16 SetupIoConnectionOriginatorToTargetConnectionPoint(
     /* an assembly object should always have an attribute 3 */
     CipAttributeStruct *attribute = GetCipAttribute(instance,
                                                     io_connection_object->consumed_path.attribute_id_or_connection_point);
-    OPENER_ASSERT(attribute != NULL);
+    OPENER_ASSERT(attribute != NULL)
     bool is_heartbeat = ( ( (CipByteArray *) attribute->data )->length == 0 );
     if ( kConnectionObjectTransportClassTriggerTransportClass1
          == ConnectionObjectGetTransportClassTriggerTransportClass(
@@ -141,12 +142,13 @@ EipUint16 SetupIoConnectionOriginatorToTargetConnectionPoint(
       data_size -= 2; /* remove 16-bit sequence count length */
       diff_size += 2;
     }
-    if ( (kOpenerConsumedDataHasRunIdleHeader) && (data_size > 0)
-         && (!is_heartbeat) ) {
+#ifdef OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER
+    if ( (data_size > 0) && (!is_heartbeat) ) {
       /* we only have an run idle header if it is not an heartbeat connection */
       data_size -= 4; /* remove the 4 bytes needed for run/idle header */
       diff_size += 4;
     }
+#endif
     if ( ( (CipByteArray *) attribute->data )->length != data_size ) {
       /*wrong connection size */
       connection_object->correct_originator_to_target_size =
@@ -231,7 +233,7 @@ EipUint16 SetupIoConnectionTargetToOriginatorConnectionPoint(
     io_connection_object->produced_path.attribute_id_or_connection_point = 3;
     CipAttributeStruct *attribute = GetCipAttribute(instance,
                                                     io_connection_object->produced_path.attribute_id_or_connection_point);
-    OPENER_ASSERT(attribute != NULL);
+    OPENER_ASSERT(attribute != NULL)
     bool is_heartbeat = ( ( (CipByteArray *) attribute->data )->length == 0 );
     if ( kConnectionObjectTransportClassTriggerTransportClass1 ==
          ConnectionObjectGetTransportClassTriggerTransportClass(
@@ -240,12 +242,13 @@ EipUint16 SetupIoConnectionTargetToOriginatorConnectionPoint(
       data_size -= 2; /* remove 16-bit sequence count length */
       diff_size += 2;
     }
-    if ( (kOpenerProducedDataHasRunIdleHeader) && (data_size > 0)
-         && (!is_heartbeat) ) {
+#ifdef OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER
+    if ( (data_size > 0) && (!is_heartbeat) ) {
       /* we only have an run idle header if it is not an heartbeat connection */
       data_size -= 4; /* remove the 4 bytes needed for run/idle header */
       diff_size += 4;
     }
+#endif
     if ( ( (CipByteArray *) attribute->data )->length != data_size ) {
       /*wrong connection size*/
       connection_object->correct_target_to_originator_size =
@@ -301,7 +304,7 @@ EipStatus EstablishIoConnection(
   OPENER_ASSERT( !(originator_to_target_connection_type ==
                    kConnectionObjectConnectionTypeNull &&
                    target_to_originator_connection_type ==
-                   kConnectionObjectConnectionTypeNull) );
+                   kConnectionObjectConnectionTypeNull) )
 
   io_connection_object->consuming_instance = NULL;
   io_connection_object->consumed_connection_path_length = 0;
@@ -456,6 +459,12 @@ EipStatus OpenProducingMulticastConnection(
     j = 1;
   }
 
+  int port = htons(kOpenerEipIoUdpPort);
+  if(kCipItemIdSocketAddressInfoTargetToOriginator !=
+     common_packet_format_data->address_info_item[j].type_id) {
+    port = common_packet_format_data->address_info_item[j].sin_port;
+  }
+
   common_packet_format_data->address_info_item[j].type_id =
     kCipItemIdSocketAddressInfoTargetToOriginator;
 
@@ -491,7 +500,7 @@ EipStatus OpenProducingMulticastConnection(
   connection_object->remote_address.sin_family = AF_INET;
   connection_object->remote_address.sin_port = common_packet_format_data
                                                ->address_info_item[j].sin_port
-                                                 = htons(kOpenerEipIoUdpPort);
+                                                 = port;
   connection_object->remote_address.sin_addr.s_addr = common_packet_format_data
                                                       ->address_info_item[j].
                                                       sin_addr =
@@ -504,11 +513,11 @@ EipStatus OpenProducingMulticastConnection(
   return kEipStatusOk;
 }
 
-/** @brief Open a Multicast connection dependent on @var direction.
+/** @brief Open a Multicast connection dependent on @p direction.
  *
  * @param direction Flag to indicate if consuming or producing.
- * @param connection_object  pointer to registered Object in ConnectionManager.
- * @param common_packet_format_data     received CPF Data Item.
+ * @param connection_object Pointer to registered Object in ConnectionManager.
+ * @param common_packet_format_data Received CPF Data Item.
  * @return kEipStatusOk on success, otherwise kEipStatusError
  */
 EipStatus OpenMulticastConnection(
@@ -542,12 +551,10 @@ EipStatus OpenMulticastConnection(
   }
 
   if(kUdpCommuncationDirectionConsuming == direction) {
-    //OPENER_ASSERT(-1 != address_info_item_which_contains_o_to_t);
     j = address_info_item_which_contains_o_to_t;
   }
 
   if(kUdpCommuncationDirectionProducing == direction) {
-    //OPENER_ASSERT(-1 != address_info_item_which_contains_o_to_t);
     j = address_info_item_which_contains_t_to_o;
   }
 
@@ -620,7 +627,7 @@ EipUint16 HandleConfigData(CipConnectionObject *connection_object) {
     assembly_class, connection_object->configuration_path.instance_id);
 
   if (0 != g_config_data_length) {
-    OPENER_ASSERT(NULL != config_instance);
+    OPENER_ASSERT(NULL != config_instance)
     if ( ConnectionWithSameConfigPointExists(
            connection_object->configuration_path.instance_id) ) {
       /* there is a connected connection with the same config point
@@ -628,10 +635,10 @@ EipUint16 HandleConfigData(CipConnectionObject *connection_object) {
       CipAttributeStruct *attribute_three = GetCipAttribute(
         config_instance,
         3);
-      OPENER_ASSERT(NULL != attribute_three);
-      CipByteArray *attribute_three_data =
+      OPENER_ASSERT(NULL != attribute_three)
+      CipByteArray * attribute_three_data =
         (CipByteArray *) attribute_three->data;
-      OPENER_ASSERT(NULL != attribute_three_data);
+      OPENER_ASSERT(NULL != attribute_three_data)
       if (attribute_three_data->length != g_config_data_length) {
         connection_manager_status =
           kConnectionManagerExtendedStatusCodeErrorOwnershipConflict;
@@ -668,6 +675,8 @@ void CloseIoConnection(CipConnectionObject *connection_object) {
   CheckIoConnectionEvent(connection_object->consumed_path.instance_id,
                          connection_object->produced_path.instance_id,
                          kIoConnectionEventClosed);
+  ConnectionObjectSetState(connection_object,
+                           kConnectionObjectStateNonExistent);
 
   if ( kConnectionObjectInstanceTypeIOExclusiveOwner ==
        ConnectionObjectGetInstanceType(connection_object)
@@ -678,12 +687,15 @@ void CloseIoConnection(CipConnectionObject *connection_object) {
          && (kEipInvalidSocket
              != connection_object->socket[kUdpCommuncationDirectionProducing]) )
     {
+      OPENER_TRACE_INFO(
+        "Exclusive Owner or Input Only connection closed - Instance type :%d\n",
+        ConnectionObjectGetInstanceType(connection_object) );
       CipConnectionObject *next_non_control_master_connection =
         GetNextNonControlMasterConnection(
           connection_object->produced_path.instance_id);
       if (NULL != next_non_control_master_connection) {
 
-        /* Transfer socket ownership */
+        OPENER_TRACE_INFO("Transfer socket ownership\n");
         next_non_control_master_connection->socket[
           kUdpCommuncationDirectionProducing] =
           connection_object->socket[kUdpCommuncationDirectionProducing];
@@ -719,6 +731,7 @@ void HandleIoConnectionTimeOut(CipConnectionObject *connection_object) {
                          connection_object->consumed_path.instance_id,
                          kIoConnectionEventTimedOut);
 
+  ConnectionObjectSetState(connection_object, kConnectionObjectStateTimedOut);
   if(connection_object->last_package_watchdog_timer ==
      connection_object->inactivity_watchdog_timer) {
     CheckForTimedOutConnectionsAndCloseTCPConnections(connection_object,
@@ -770,7 +783,8 @@ EipStatus SendConnectedData(CipConnectionObject *connection_object) {
   /* TODO think of adding an own send buffer to each connection object in order to preset up the whole message on connection opening and just change the variable data items e.g., sequence number */
 
   CipCommonPacketFormatData *common_packet_format_data =
-    &g_common_packet_format_data_item;                                                      /* TODO think on adding a CPF data item to the S_CIP_ConnectionObject in order to remove the code here or even better allocate memory in the connection object for storing the message to send and just change the application data*/
+    &g_common_packet_format_data_item;
+  /* TODO think on adding a CPF data item to the S_CIP_ConnectionObject in order to remove the code here or even better allocate memory in the connection object for storing the message to send and just change the application data*/
 
   connection_object->eip_level_sequence_count_producing++;
 
@@ -809,43 +823,48 @@ EipStatus SendConnectedData(CipConnectionObject *connection_object) {
   common_packet_format_data->address_info_item[0].type_id = 0;
   common_packet_format_data->address_info_item[1].type_id = 0;
 
+  ENIPMessage outgoing_message = {0};
+  InitializeENIPMessage(&outgoing_message);
   EipUint16 reply_length = AssembleIOMessage(common_packet_format_data,
-                                             &g_message_data_reply_buffer[0]);
+                                             &outgoing_message);
+
 
-  EipUint8 *message_data_reply_buffer =
-    &g_message_data_reply_buffer[reply_length - 2];
+  outgoing_message.current_message_position -= 2;
   common_packet_format_data->data_item.length = producing_instance_attributes
                                                 ->length;
-  if (kOpenerProducedDataHasRunIdleHeader) {
-    common_packet_format_data->data_item.length += 4;
-  }
+#ifdef OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER
+  common_packet_format_data->data_item.length += 4;
+#endif /* OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER */
 
   if (kConnectionObjectTransportClassTriggerTransportClass1 ==
       ConnectionObjectGetTransportClassTriggerTransportClass(connection_object) )
   {
     common_packet_format_data->data_item.length += 2;
     AddIntToMessage(common_packet_format_data->data_item.length,
-                    &message_data_reply_buffer);
+                    &outgoing_message.current_message_position);
     AddIntToMessage(connection_object->sequence_count_producing,
-                    &message_data_reply_buffer);
+                    &outgoing_message.current_message_position);
   } else {
     AddIntToMessage(common_packet_format_data->data_item.length,
-                    &message_data_reply_buffer);
+                    &outgoing_message.current_message_position);
   }
 
-  if (kOpenerProducedDataHasRunIdleHeader) {
-    AddDintToMessage( g_run_idle_state, &(message_data_reply_buffer) );
-  }
+#ifdef OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER
+  AddDintToMessage( g_run_idle_state,
+                    &(outgoing_message.current_message_position) );
+#endif /* OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER */
 
-  memcpy(message_data_reply_buffer, producing_instance_attributes->data,
+  memcpy(outgoing_message.current_message_position,
+         producing_instance_attributes->data,
          producing_instance_attributes->length);
 
-  reply_length += common_packet_format_data->data_item.length;
+  outgoing_message.used_message_length +=
+    common_packet_format_data->data_item.length;
 
   return SendUdpData(
     &connection_object->remote_address,
     connection_object->socket[kUdpCommuncationDirectionProducing],
-    &g_message_data_reply_buffer[0], reply_length);
+    outgoing_message.message_buffer, outgoing_message.used_message_length);
 }
 
 EipStatus HandleReceivedIoConnectionData(
@@ -854,6 +873,8 @@ EipStatus HandleReceivedIoConnectionData(
   EipUint16 data_length
   ) {
 
+  OPENER_TRACE_INFO("Starting data length: %d\n", data_length);
+  bool no_new_data = false;
   /* check class 1 sequence number*/
   if (kConnectionObjectTransportClassTriggerTransportClass1 ==
       ConnectionObjectGetTransportClassTriggerTransportClass(connection_object) )
@@ -861,21 +882,33 @@ EipStatus HandleReceivedIoConnectionData(
     EipUint16 sequence_buffer = GetIntFromMessage( &(data) );
     if ( SEQ_LEQ16(sequence_buffer,
                    connection_object->sequence_count_consuming) ) {
-      return kEipStatusOk; /* no new data for the assembly */
+      no_new_data = true;
     }
     connection_object->sequence_count_consuming = sequence_buffer;
     data_length -= 2;
   }
 
+  OPENER_TRACE_INFO("data length after sequence count: %d\n", data_length);
   if (data_length > 0) {
     /* we have no heartbeat connection */
-    if (kOpenerConsumedDataHasRunIdleHeader) {
-      EipUint32 nRunIdleBuf = GetDintFromMessage( &(data) );
-      if (g_run_idle_state != nRunIdleBuf) {
-        RunIdleChanged(nRunIdleBuf);
-      }
-      g_run_idle_state = nRunIdleBuf;
-      data_length -= 4;
+#ifdef OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER
+    EipUint32 nRunIdleBuf = GetDintFromMessage( &(data) );
+    OPENER_TRACE_INFO("Run/Idle handler: 0x%x", nRunIdleBuf);
+    const uint32_t kRunBitMask = 0x0001;
+    if( (kRunBitMask & nRunIdleBuf) == 1 ) {
+      CipIdentitySetExtendedDeviceStatus(kAtLeastOneIoConnectionInRunMode);
+    } else {
+      CipIdentitySetExtendedDeviceStatus(
+        kAtLeastOneIoConnectionEstablishedAllInIdleMode);
+    }
+    if (g_run_idle_state != nRunIdleBuf) {
+      RunIdleChanged(nRunIdleBuf);
+    }
+    g_run_idle_state = nRunIdleBuf;
+    data_length -= 4;
+#endif /* OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER */
+    if(no_new_data) {
+      return kEipStatusOk;
     }
 
     if (NotifyAssemblyConnectedDataReceived(

+ 8 - 8
source/src/cip/cipmessagerouter.c

@@ -21,17 +21,17 @@ CipMessageRouterResponse g_message_router_response;
  * memory. The size of the array could be a parameter in the platform config file.
  */
 typedef struct cip_message_router_object {
-  struct cip_message_router_object *next; /*< link */
-  CipClass *cip_class; /*< object */
+  struct cip_message_router_object *next; /**< link */
+  CipClass *cip_class; /**< object */
 } CipMessageRouterObject;
 
 /** @brief Pointer to first registered object in MessageRouter*/
 CipMessageRouterObject *g_first_object = NULL;
 
-/** @brief Register an Class to the message router
+/** @brief Register a CIP Class to the message router
  *  @param cip_class Pointer to a class object to be registered.
- *  @return status      0 .. success
- *                     -1 .. error no memory available to register more objects
+ *  @return kEipStatusOk on success
+ *          kEipStatusError on no memory available to register more objects
  */
 EipStatus RegisterCipClass(CipClass *cip_class);
 
@@ -113,7 +113,7 @@ CipMessageRouterObject *GetRegisteredObject(EipUint32 class_id) {
 
   while (NULL != object) /* for each entry in list*/
   {
-    OPENER_ASSERT(object->cip_class != NULL);
+    OPENER_ASSERT(NULL != object->cip_class)
     if (object->cip_class->class_id == class_id) {
       return object; /* return registration node if it matches class ID*/
     }
@@ -172,7 +172,7 @@ EipStatus RegisterCipClass(CipClass *cip_class) {
 
 EipStatus NotifyMessageRouter(EipUint8 *data,
                               int data_length,
-                              struct sockaddr *originator_address,
+                              const struct sockaddr *const originator_address,
                               const int encapsulation_session) {
   EipStatus eip_status = kEipStatusOkSend;
   EipByte status = kCipErrorSuccess;
@@ -212,7 +212,7 @@ EipStatus NotifyMessageRouter(EipUint8 *data,
       /* call notify function from Object with ClassID (gMRRequest.RequestPath.ClassID)
          object will or will not make an reply into gMRResponse*/
       g_message_router_response.reserved = 0;
-      OPENER_ASSERT(NULL != registered_object->cip_class);
+      OPENER_ASSERT(NULL != registered_object->cip_class)
       OPENER_TRACE_INFO(
         "NotifyMessageRouter: calling notify function of class '%s'\n",
         registered_object->cip_class->class_name);

+ 8 - 6
source/src/cip/cipmessagerouter.h

@@ -35,12 +35,14 @@ void DeleteAllClasses(void);
  *  g_stCPFDataItem.
  *  @param data pointer to the data buffer of the message directly at the beginning of the CIP part.
  *  @param data_length number of bytes in the data buffer
- *  @return  EIP_ERROR on fault
- *           EIP_OK on success
+ *  @param originator_address The address of the originator as received
+ *  @param encapsulation_session The associated encapsulation session of the explicit message
+ *  @return  kEipStatusError on fault
+ *           kEipStatusOk on success
  */
 EipStatus NotifyMessageRouter(EipUint8 *data,
                               int data_length,
-                              struct sockaddr *originator_address,
+                              const struct sockaddr *const originator_address,
                               const int encapsulation_session);
 
 /*! Register a class at the message router.
@@ -48,9 +50,9 @@ EipStatus NotifyMessageRouter(EipUint8 *data,
  *  explicit messages each class has to register.
  *  Will be automatically done when invoking create
  *  createCIPClass.
- *  @param object CIP class to be registered
- *  @return EIP_OK on success
+ *  @param cip_class CIP class to be registered
+ *  @return kEipStatusOk on success
  */
-EipStatus RegisterCipClass(CipClass *object);
+EipStatus RegisterCipClass(CipClass *cip_class);
 
 #endif /* OPENER_CIPMESSAGEROUTER_H_ */

+ 13 - 16
source/src/cip/cipqos.c

@@ -89,32 +89,29 @@ EipStatus SetAttributeSingleQoS(
 }
 
 CipUsint GetPriorityForSocket(ConnectionObjectPriority priority) {
+
+  CipUsint priority_value = dscp_explicit;
   switch (priority) {
-    case kConnectionObjectPriorityLow: {
-      return dscp_low;
+    case kConnectionObjectPriorityLow:
+      priority_value = dscp_low;
       break;
-    }
-    case kConnectionObjectPriorityHigh: {
-      return dscp_high;
+    case kConnectionObjectPriorityHigh:
+      priority_value = dscp_high;
       break;
-    }
-    case kConnectionObjectPriorityScheduled: {
-      return dscp_scheduled;
+    case kConnectionObjectPriorityScheduled:
+      priority_value = dscp_scheduled;
       break;
-    }
-    case kConnectionObjectPriorityUrgent: {
-      return dscp_urgent;
+    case kConnectionObjectPriorityUrgent:
+      priority_value = dscp_urgent;
       break;
-    }
-    default: {
-      return dscp_explicit;
+    default:
+      priority_value = dscp_explicit;
       break;
-    }
   }
+  return priority_value;
 }
 
 void InitializeCipQos(CipClass *class) {
-
   CipClass *meta_class = class->class_instance.cip_class;
 }
 

+ 2 - 3
source/src/cip/ciptcpipinterface.c

@@ -232,7 +232,6 @@ EipStatus GetAttributeSingleTcpIpInterface(
   struct sockaddr *originator_address,
   const int encapsulation_session) {
 
-  EipStatus status = kEipStatusOkSend;
   EipByte *message = message_router_response->data;
 
   message_router_response->data_length = 0;
@@ -384,8 +383,8 @@ EipStatus GetAttributeAllTcpIpInterface(
 
 EipUint16 GetEncapsulationInactivityTimeout(CipInstance *instance) {
   CipAttributeStruct *attribute = GetCipAttribute(instance, 13);
-  OPENER_ASSERT(NULL != attribute);
-  CipUint *data = (CipUint *) attribute->data;
+  OPENER_ASSERT(NULL != attribute)
+  CipUint * data = (CipUint *) attribute->data;
   EipUint16 encapsulation_inactivity_timeout = *data;
   return encapsulation_inactivity_timeout;
 }

+ 11 - 12
source/src/cip/ciptypes.h

@@ -8,6 +8,9 @@
 
 #include "typedefs.h"
 #include "networkhandler.h"
+#include "enipmessage.h"
+
+#include "opener_user_conf.h"
 
 /** @brief Enum containing the encoding values for CIP data types for CIP
  * Messages */
@@ -255,18 +258,20 @@ typedef struct cip_class {
 } CipClass;
 
 /** @ingroup CIP_API
- *  @typedef  EIP_STATUS (*TCIPServiceFunc)(S_CIP_Instance *pa_pstInstance,
- *   S_CIP_MR_Request *pa_MRRequest, S_CIP_MR_Response *pa_MRResponse)
+ *  @typedef  EipStatus (*CipServiceFunction)(CipInstance *const instance,
+ *    CipMessageRouterRequest *const message_router_request,
+ *    CipMessageRouterResponse *const message_router_response,
+ *    struct sockaddr *originator_address, const int encapsulation_session)
  *  @brief Signature definition for the implementation of CIP services.
  *
  *  CIP services have to follow this signature in order to be handled correctly
  *   by the stack.
- *  @param pa_pstInstance the instance which was referenced in the service
+ *  @param instance the instance which was referenced in the service
  *   request
- *  @param pa_MRRequest request data
- *  @param pa_MRResponse storage for the response data, including a buffer for
+ *  @param message_router_request request data
+ *  @param message_router_response storage for the response data, including a buffer for
  *   extended data
- *  @return EIP_OK_SEND if service could be executed successfully and a response
+ *  @return kEipOkSend if service could be executed successfully and a response
  *   should be sent
  */
 typedef EipStatus (*CipServiceFunction)(
@@ -312,12 +317,6 @@ typedef struct {
   void *data;
 } CipUnconnectedSendParameter;
 
-/** @brief Data of an CIP Ethernet Link object */
-//typedef struct {
-//  EipUint32 interface_speed; /**< 10/100/1000 Mbit/sec */
-//  EipUint32 interface_flags; /**< Inferface flags as defined in the CIP specification */
-//  EipUint8 physical_address[6]; /**< MAC address of the Ethernet link */
-//} CipEthernetLinkObject;
 typedef struct {
   CipUint num_conn_entries;
   CipBool *conn_open_bits;

+ 2 - 0
source/src/enet_encap/CMakeLists.txt

@@ -15,3 +15,5 @@ opener_common_includes()
 opener_platform_support("INCLUDES")
 
 add_library( ENET_ENCAP ${ENET_ENCAP_SRC} )
+
+target_link_libraries( ENET_ENCAP Utils)

+ 290 - 245
source/src/enet_encap/cpf.c

@@ -14,6 +14,7 @@
 #include "ciperror.h"
 #include "cipconnectionmanager.h"
 #include "trace.h"
+#include "encap.h"
 
 const size_t item_count_field_size = 2; /**< The size of the item count field in the message */
 const size_t item_data_type_id_field_length = 2; /**< The size of the item count field in the message */
@@ -22,15 +23,16 @@ const size_t sequenced_address_item_length = 8;
 
 CipCommonPacketFormatData g_common_packet_format_data_item; /**< CPF global data items */
 
-int NotifyCommonPacketFormat(EncapsulationData *const receive_data,
-                             EipUint8 *reply_buffer,
-                             struct sockaddr *originator_address) {
+int NotifyCommonPacketFormat(EncapsulationData *const received_data,
+                             const struct sockaddr *const originator_address,
+                             ENIPMessage *const outgoing_message) {
   int return_value = kEipStatusError;
 
-  if ( ( return_value = CreateCommonPacketFormatStructure(
-           receive_data->current_communication_buffer_position,
-           receive_data->data_length, &g_common_packet_format_data_item) )
-       == kEipStatusError ) {
+  if (kEipStatusError == ( return_value = CreateCommonPacketFormatStructure(
+                             received_data->
+                             current_communication_buffer_position,
+                             received_data->data_length,
+                             &g_common_packet_format_data_item) ) ) {
     OPENER_TRACE_ERR("notifyCPF: error from createCPFstructure\n");
   } else {
     return_value = kEipStatusOk; /* In cases of errors we normally need to send an error response */
@@ -43,30 +45,53 @@ int NotifyCommonPacketFormat(EncapsulationData *const receive_data,
           g_common_packet_format_data_item.data_item.data,
           g_common_packet_format_data_item.data_item.length,
           originator_address,
-          receive_data->session_handle);
+          received_data->session_handle);
         if (return_value != kEipStatusError) {
+          SkipEncapsulationHeader(outgoing_message);
           return_value = AssembleLinearMessage(
             &g_message_router_response, &g_common_packet_format_data_item,
-            reply_buffer);
+            outgoing_message);
+
+          CipOctet *buffer = outgoing_message->current_message_position;
+          outgoing_message->current_message_position =
+            outgoing_message->message_buffer;
+          GenerateEncapsulationHeader(received_data,
+                                      return_value,
+                                      received_data->session_handle,
+                                      kEncapsulationProtocolSuccess,
+                                      outgoing_message);
+          outgoing_message->current_message_position = buffer;
+          return_value = outgoing_message->used_message_length;
         }
       } else {
         /* wrong data item detected*/
         OPENER_TRACE_ERR(
           "notifyCPF: got something besides the expected CIP_ITEM_ID_UNCONNECTEDMESSAGE\n");
-        receive_data->status = kEncapsulationProtocolIncorrectData;
+        GenerateEncapsulationHeader(received_data,
+                                    return_value,
+                                    received_data->session_handle,
+                                    kEncapsulationProtocolIncorrectData,
+                                    outgoing_message);
+        return_value = outgoing_message->used_message_length;
       }
     } else {
       OPENER_TRACE_ERR(
         "notifyCPF: got something besides the expected CIP_ITEM_ID_NULL\n");
-      receive_data->status = kEncapsulationProtocolIncorrectData;
+      GenerateEncapsulationHeader(received_data,
+                                  return_value,
+                                  received_data->session_handle,
+                                  kEncapsulationProtocolIncorrectData,
+                                  outgoing_message);
+      return_value = outgoing_message->used_message_length;
     }
   }
   return return_value;
 }
 
-int NotifyConnectedCommonPacketFormat(EncapsulationData *received_data,
-                                      EipUint8 *reply_buffer,
-                                      struct sockaddr *originator_address) {
+int NotifyConnectedCommonPacketFormat(
+  const EncapsulationData *const received_data,
+  const struct sockaddr *const originator_address,
+  ENIPMessage *const outgoing_message) {
 
   int return_value = CreateCommonPacketFormatStructure(
     received_data->current_communication_buffer_position,
@@ -86,12 +111,41 @@ int NotifyConnectedCommonPacketFormat(EncapsulationData *received_data,
         /* reset the watchdog timer */
         ConnectionObjectResetInactivityWatchdogTimerValue(connection_object);
 
-        /*TODO check connection id  and sequence count    */
+        /*TODO check connection id  and sequence count */
         if (g_common_packet_format_data_item.data_item.type_id
             == kCipItemIdConnectedDataItem) { /* connected data item received*/
           EipUint8 *buffer = g_common_packet_format_data_item.data_item.data;
           g_common_packet_format_data_item.address_item.data.sequence_number =
             (EipUint32) GetIntFromMessage( (const EipUint8 **const)&buffer );
+          OPENER_TRACE_INFO(
+            "Class 3 sequence number: %d, last sequence number: %d\n",
+            g_common_packet_format_data_item.address_item.data.sequence_number,
+            connection_object->sequence_count_consuming);
+          if(connection_object->sequence_count_consuming ==
+             g_common_packet_format_data_item.address_item.data.sequence_number)
+          {
+            memcpy(outgoing_message,
+                   &(connection_object->last_reply_sent),
+                   sizeof(ENIPMessage) );
+            outgoing_message->current_message_position =
+              outgoing_message->message_buffer;
+            /* Regenerate encapsulation header for new message */
+            outgoing_message->used_message_length -=
+              ENCAPSULATION_HEADER_LENGTH;
+            GenerateEncapsulationHeader(received_data,
+                                        outgoing_message->used_message_length,
+                                        received_data->session_handle,
+                                        kEncapsulationProtocolSuccess,
+                                        outgoing_message);
+            outgoing_message->current_message_position = buffer;
+            /* End regenerate encapsulation header for new message */
+            return outgoing_message->used_message_length;
+          }
+          connection_object->sequence_count_consuming =
+            g_common_packet_format_data_item.address_item.data.sequence_number;
+
+          ConnectionObjectResetInactivityWatchdogTimerValue(connection_object);
+
           return_value = NotifyMessageRouter(
             buffer,
             g_common_packet_format_data_item.data_item.length - 2,
@@ -102,9 +156,24 @@ int NotifyConnectedCommonPacketFormat(EncapsulationData *received_data,
             g_common_packet_format_data_item.address_item.data
             .connection_identifier = connection_object
                                      ->cip_produced_connection_id;
+            SkipEncapsulationHeader(outgoing_message);
             return_value = AssembleLinearMessage(
               &g_message_router_response, &g_common_packet_format_data_item,
-              reply_buffer);
+              outgoing_message);
+
+            CipOctet *buffer = outgoing_message->current_message_position;
+            outgoing_message->current_message_position =
+              outgoing_message->message_buffer;
+            GenerateEncapsulationHeader(received_data,
+                                        return_value,
+                                        received_data->session_handle,
+                                        kEncapsulationProtocolSuccess,
+                                        outgoing_message);
+            outgoing_message->current_message_position = buffer;
+            memcpy(&connection_object->last_reply_sent,
+                   outgoing_message,
+                   sizeof(ENIPMessage) );
+            return_value = outgoing_message->used_message_length;
           }
         } else {
           /* wrong data item detected*/
@@ -120,7 +189,7 @@ int NotifyConnectedCommonPacketFormat(EncapsulationData *received_data,
         "notifyConnectedCPF: got something besides the expected CIP_ITEM_ID_NULL\n");
     }
   }
-  return return_value;
+  return outgoing_message->used_message_length;
 }
 
 /**
@@ -142,7 +211,7 @@ EipStatus CreateCommonPacketFormatStructure(
 
   int length_count = 0;
   CipUint item_count = GetIntFromMessage(&data);
-  OPENER_ASSERT(4U >= item_count); /* Sanitizing data - probably needs to be changed for productive code */
+  OPENER_ASSERT(4U >= item_count) /* Sanitizing data - probably needs to be changed for productive code */
   common_packet_format_data->item_count = item_count;
   length_count += 2;
   if (common_packet_format_data->item_count >= 1U) {
@@ -221,267 +290,266 @@ EipStatus CreateCommonPacketFormatStructure(
 
 /**
  * @brief Encodes a Null Address Item into the message frame
- * @param message The message frame
- * @param size The actual size of the message frame
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message frame after encoding
  */
-int EncodeNullAddressItem(EipUint8 **message,
-                          int size) {
-  size += AddIntToMessage(kCipItemIdNullAddress, message);
+int EncodeNullAddressItem(ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
+    kCipItemIdNullAddress,
+    &outgoing_message->current_message_position);
   /* null address item -> address length set to 0 */
-  size += AddIntToMessage(0, message);
-  return size;
+  outgoing_message->used_message_length += AddIntToMessage(0,
+                                                           &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * Encodes a Connected Address Item into the message frame
- * @param message The message frame
  * @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
- * @param size The actual size of the message frame
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message frame after encoding
  */
 int EncodeConnectedAddressItem(
-  EipUint8 **message,
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  int size) {
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
   /* connected data item -> address length set to 4 and copy ConnectionIdentifier */
-  size += AddIntToMessage(kCipItemIdConnectionAddress, message);
-  size += AddIntToMessage(4, message);
-  size += AddDintToMessage(
+  outgoing_message->used_message_length += AddIntToMessage(
+    kCipItemIdConnectionAddress,
+    &outgoing_message->current_message_position);
+  outgoing_message->used_message_length += AddIntToMessage(4,
+                                                           &outgoing_message->current_message_position);
+  outgoing_message->used_message_length += AddDintToMessage(
     common_packet_format_data_item->address_item.data.connection_identifier,
-    message);
-  return size;
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes a sequenced address item into the message
  *
- * @param message Pointer to the message memory start
  * @param common_packet_format_data_item Common Packet Format item which is used in the encoding
- * @param size Size of the message at the start of the function
+ * @param outgoing_message The outgoing message object
  *
  * @return New message size after encoding
  */
 int EncodeSequencedAddressItem(
-  EipUint8 **message,
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  int size) {
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
   /* sequenced address item -> address length set to 8 and copy ConnectionIdentifier and SequenceNumber */
-  size += AddIntToMessage(kCipItemIdSequencedAddressItem, message);
-  size += AddIntToMessage(sequenced_address_item_length, message);
-  size += AddDintToMessage(
+  outgoing_message->used_message_length += AddIntToMessage(
+    kCipItemIdSequencedAddressItem,
+    &outgoing_message->current_message_position);
+  outgoing_message->used_message_length += AddIntToMessage(
+    sequenced_address_item_length,
+    &outgoing_message->current_message_position);
+  outgoing_message->used_message_length += AddDintToMessage(
     common_packet_format_data_item->address_item.data.connection_identifier,
-    message);
-  size += AddDintToMessage(
+    &outgoing_message->current_message_position);
+  outgoing_message->used_message_length += AddDintToMessage(
     common_packet_format_data_item->address_item.data.sequence_number,
-    message);
-  return size;
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Adds the item count to the message frame
  *
  * @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
- * @param message The message frame
- * @param size The actual size of the message frame
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message frame after encoding
  */
-int EncodeItemCount(CipCommonPacketFormatData *common_packet_format_data_item,
-                    EipUint8 **message,
-                    int size) {
-  size += AddIntToMessage(common_packet_format_data_item->item_count, message); /* item count */
-  return size;
+int EncodeItemCount(
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
+    common_packet_format_data_item->item_count,
+    &outgoing_message->current_message_position);                                                                                                    /* item count */
+  return outgoing_message->used_message_length;
 }
 
 /**
  * Adds the data item type to the message frame
  *
  * @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
- * @param message The message frame
- * @param size The actual size of the message frame
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message frame after encoding
  */
 int EncodeDataItemType(
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 **message,
-  int size) {
-  size += AddIntToMessage(common_packet_format_data_item->data_item.type_id,
-                          message);
-  return size;
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
+    common_packet_format_data_item->data_item.type_id,
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * Adds the data item section length to the message frame
  *
  * @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
- * @param message The message frame
- * @param size The actual size of the message frame
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message frame after encoding
  */
 int EncodeDataItemLength(
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 **message,
-  int size) {
-  size += AddIntToMessage(common_packet_format_data_item->data_item.length,
-                          message);
-  return size;
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
+    common_packet_format_data_item->data_item.length,
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * Adds the data items to the message frame
  *
  * @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
- * @param message Message frame to which the data is added
- * @param size The actual size of the message frame
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message frame after encoding
  */
 int EncodeDataItemData(
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 **message,
-  int size) {
-  for (int i = 0; i < common_packet_format_data_item->data_item.length; i++) {
-    size += AddSintToMessage(
-      *(common_packet_format_data_item->data_item.data + i), message);
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
+  for (size_t i = 0; i < common_packet_format_data_item->data_item.length;
+       i++) {
+    outgoing_message->used_message_length += AddSintToMessage(
+      *(common_packet_format_data_item->data_item.data + i),
+      &outgoing_message->current_message_position);
   }
-  return size;
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the Connected Data item length
  *
  * @param message_router_response The Router Response message which shall be answered
- * @param message Message frame to which the data is added
- * @param size Current size of the message buffer
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message buffer
  */
 
 int EncodeConnectedDataItemLength(
-  CipMessageRouterResponse *message_router_response,
-  EipUint8 **message,
-  int size) {
-  size += AddIntToMessage(
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
     (EipUint16) ( message_router_response->data_length + 4 + 2  /* TODO: Magic numbers */
                   + (2 * message_router_response->size_of_additional_status) ),
-    message);
-  return size;
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes a sequence number into the message
  *
- * @param size The current size of the message buffer
  * @param common_packet_format_data_item
- * @param message Message frame to which the data is added
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message buffer
  *
  */
 int EncodeSequenceNumber(
-  int size,
-  const CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 **message) {
-  size += AddIntToMessage(
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
     (EipUint16) common_packet_format_data_item->address_item.data
     .sequence_number,
-    message);
-  return size;
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the reply service code for the requested service
  *
- * @param size The current size of the message buffer
- * @param message Message frame to which the data is added
- * @param message_rounter_response The router response message data structure to be processed
+ * @param message_router_response The router response message data structure to be processed
+ * @param outgoing_message The outgoing message object
  *
  * @return The new size of the message buffer
  */
-int EncodeReplyService(int size,
-                       EipUint8 **message,
-                       CipMessageRouterResponse *message_router_response) {
-  size += AddSintToMessage(message_router_response->reply_service, message);
-  return size;
+int EncodeReplyService(
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddSintToMessage(
+    message_router_response->reply_service,
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the reserved byte in the message router response
  *
- * @param size Current size of the message buffer
- * @param message Message frame to which the data is added
  * @param message_router_response Router Response message to be processed
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
 int EncodeReservedFieldOfLengthByte(
-  int size,
-  EipUint8 **message,
-  CipMessageRouterResponse *message_router_response) {
-  size += AddSintToMessage(message_router_response->reserved, message);
-  return size;
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddSintToMessage(
+    message_router_response->reserved,
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the general status of a Router Response
  *
- * @param size Current size of the message buffer
- * @param message Message frame to which the data is added
  * @param message_router_response Router Response message to be processed
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
-int EncodeGeneralStatus(int size,
-                        EipUint8 **message,
-                        CipMessageRouterResponse *message_router_response) {
-  size += AddSintToMessage(message_router_response->general_status, message);
-  return size;
+int EncodeGeneralStatus(
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddSintToMessage(
+    message_router_response->general_status,
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the length of the extended status data part
  *
- * @param size Current size of the message buffer
- * @param message Message frame to which the data is added
  * @param message_router_response Router Response message to be processed
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
 
 int EncodeExtendedStatusLength(
-  int size,
-  EipUint8 **message,
-  CipMessageRouterResponse *message_router_response) {
-  size += AddSintToMessage(message_router_response->size_of_additional_status,
-                           message);
-  return size;
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddSintToMessage(
+    message_router_response->size_of_additional_status,
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the extended status data items
  *
- * @param size Current size of the message buffer
  * @param message_router_response Router Response message to be processed
- * @param message Message frame to which the data is added
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
-int EncodeExtendedStatusDataItems(
-  int size,
-  CipMessageRouterResponse *message_router_response,
-  EipUint8 **message) {
-  for (int i = 0; i < message_router_response->size_of_additional_status;
-       i++) {
-    size += AddIntToMessage(message_router_response->additional_status[i],
-                            message);
+size_t EncodeExtendedStatusDataItems(
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  for (size_t i = 0;
+       i < message_router_response->size_of_additional_status &&
+       i < MAX_SIZE_OF_ADD_STATUS; i++) {
+    outgoing_message->used_message_length += AddIntToMessage(
+      message_router_response->additional_status[i],
+      &outgoing_message->current_message_position);
   }
-
-  return size;
+  return outgoing_message->used_message_length;
 }
 
 /**
@@ -490,147 +558,130 @@ int EncodeExtendedStatusDataItems(
  * This function uses EncodeExtendedStatusLength and EncodeExtendedStatusDataItems
  * to encode the complete extended status information into the message
  *
- * @param size Current size of the message buffer
- * @param message Message frame to which the data is added
  * @param message_router_response Router Response message to be processed
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
 
-int EncodeExtendedStatus(int size,
-                         EipUint8 **message,
-                         CipMessageRouterResponse *message_router_response) {
-  size = EncodeExtendedStatusLength(size, message, message_router_response);
-  size = EncodeExtendedStatusDataItems(size, message_router_response, message);
+int EncodeExtendedStatus(
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  EncodeExtendedStatusLength(message_router_response, outgoing_message);
+  EncodeExtendedStatusDataItems(message_router_response, outgoing_message);
 
-  return size;
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encode the data item length of the unconnected data segment
  *
- * @param size Current size of the message buffer
  * @param message_router_response Router Response message to be processed
- * @param message Message frame to which the data is added
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
 int EncodeUnconnectedDataItemLength(
-  int size,
-  CipMessageRouterResponse *message_router_response,
-  EipUint8 **message) {
-  size += AddIntToMessage(
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
     (EipUint16) ( message_router_response->data_length + 4  /* TODO: Magic number */
                   + (2 * message_router_response->size_of_additional_status) ),
-    message);
-  return size;
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the Message Router Response data
  *
- * @param size Current size of the message buffer
  * @param message_router_response Router Response message to be processed
- * @param message Message frame to which the data is added
+ * @param outgoing_message The outgoing message object
  */
 int EncodeMessageRouterResponseData(
-  int size,
-  CipMessageRouterResponse *message_router_response,
-  EipUint8 **message) {
-  for (int i = 0; i < message_router_response->data_length; i++) {
-    size += AddSintToMessage( (message_router_response->data)[i], &*message );
+  const CipMessageRouterResponse *const message_router_response,
+  ENIPMessage *const outgoing_message) {
+  for (size_t i = 0; i < message_router_response->data_length; i++) {
+    outgoing_message->used_message_length +=
+      AddSintToMessage( (message_router_response->data)[i],
+                        &outgoing_message->current_message_position );
   }
-  return size;
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the sockaddr info type id into the message
  *
- * @param size Current size of the message buffer
  * @param item_type
  * @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
- * @param message Message frame to which the encoded data is added
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
 int EncodeSockaddrInfoItemTypeId(
-  int size,
   int item_type,
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 **message) {
-  OPENER_ASSERT(item_type == 0 || item_type == 1);
-  size += AddIntToMessage(
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
+  OPENER_ASSERT(item_type == 0 || item_type == 1)
+  outgoing_message->used_message_length += AddIntToMessage(
     common_packet_format_data_item->address_info_item[item_type].type_id,
-    message);
+    &outgoing_message->current_message_position);
 
-  return size;
+  return outgoing_message->used_message_length;
 }
 
 /**
  * @brief Encodes the sockaddr info length into the message
  *
- * @param size Current size of the message buffer
  * @param item_type
  * @param common_packet_format_data_item The Common Packet Format data structure from which the message is constructed
- * @param message Message frame to which the encoded data is added
+ * @param outgoing_message The outgoing message object
  *
  * @return New size of the message buffer
  */
 int EncodeSockaddrInfoLength(
-  int size,
   int item_type,
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 **message) {
-  size += AddIntToMessage(
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
+  outgoing_message->used_message_length += AddIntToMessage(
     common_packet_format_data_item->address_info_item[item_type].length,
-    message);
-  return size;
+    &outgoing_message->current_message_position);
+  return outgoing_message->used_message_length;
 }
 
-/** @brief Copy data from message_router_response struct and common_packet_format_data_item into linear memory in
- * pa_msg for transmission over in encapsulation.
- *
- * @param message_router_response	pointer to message router response which has to be aligned into linear memory.
- * @param common_packet_format_data_item pointer to CPF structure which has to be aligned into linear memory.
- * @param message		pointer to linear memory.
- *  @return length of reply in message in bytes
- *                      -1 .. error
- */
 int AssembleLinearMessage(
-  CipMessageRouterResponse *message_router_response,
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 *message) {
-
-  size_t message_size = 0;
+  const CipMessageRouterResponse *const message_router_response,
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
 
   if (message_router_response) {
     /* add Interface Handle and Timeout = 0 -> only for SendRRData and SendUnitData necessary */
-    AddDintToMessage(0, &message);
-    AddIntToMessage(0, &message);
-    message_size += 6;
+    outgoing_message->used_message_length += AddDintToMessage(0,
+                                                              &outgoing_message->current_message_position);
+    outgoing_message->used_message_length += AddIntToMessage(0,
+                                                             &outgoing_message->current_message_position);
   }
 
-  message_size = EncodeItemCount(common_packet_format_data_item, &message,
-                                 message_size);
+  EncodeItemCount(common_packet_format_data_item, outgoing_message);
 
   /* process Address Item */
   switch (common_packet_format_data_item->address_item.type_id) {
     case kCipItemIdNullAddress: {
-      message_size = EncodeNullAddressItem(&message, message_size);
+      EncodeNullAddressItem(outgoing_message);
       break;
     }
     case kCipItemIdConnectionAddress: {
-      message_size = EncodeConnectedAddressItem(&message,
-                                                common_packet_format_data_item,
-                                                message_size);
+      EncodeConnectedAddressItem(common_packet_format_data_item,
+                                 outgoing_message);
       break;
     }
     case kCipItemIdSequencedAddressItem: {
-      message_size = EncodeSequencedAddressItem(&message,
-                                                common_packet_format_data_item,
-                                                message_size);
+      EncodeSequencedAddressItem(common_packet_format_data_item,
+                                 outgoing_message);
       break;
     }
+    default:
+      OPENER_TRACE_INFO("Unknown CIP Item in AssembleLinearMessage");
+      return kEipStatusError;
   }
 
   /* process Data Item */
@@ -640,44 +691,38 @@ int AssembleLinearMessage(
            == kCipItemIdConnectedDataItem) ) {
 
     if (message_router_response) {
-      message_size = EncodeDataItemType(common_packet_format_data_item,
-                                        &message, message_size);
+      EncodeDataItemType(common_packet_format_data_item,
+                         outgoing_message);
 
       if (common_packet_format_data_item->data_item.type_id
           == kCipItemIdConnectedDataItem) { /* Connected Item */
-        message_size = EncodeConnectedDataItemLength(message_router_response,
-                                                     &message, message_size);
-        message_size = EncodeSequenceNumber(message_size,
-                                            &g_common_packet_format_data_item,
-                                            &message);
+        EncodeConnectedDataItemLength(message_router_response,
+                                      outgoing_message);
+        EncodeSequenceNumber(&g_common_packet_format_data_item,
+                             outgoing_message);
 
       } else { /* Unconnected Item */
-        message_size = EncodeUnconnectedDataItemLength(message_size,
-                                                       message_router_response,
-                                                       &message);
+        EncodeUnconnectedDataItemLength(message_router_response,
+                                        outgoing_message);
       }
 
       /* write message router response into linear memory */
-      message_size = EncodeReplyService(message_size, &message,
-                                        message_router_response);
-      message_size = EncodeReservedFieldOfLengthByte(message_size, &message,
-                                                     message_router_response);
-      message_size = EncodeGeneralStatus(message_size, &message,
-                                         message_router_response);
-      message_size = EncodeExtendedStatus(message_size, &message,
-                                          message_router_response);
-      message_size = EncodeMessageRouterResponseData(message_size,
-                                                     message_router_response,
-                                                     &message);
+      EncodeReplyService(message_router_response, outgoing_message);
+      EncodeReservedFieldOfLengthByte(message_router_response,
+                                      outgoing_message);
+      EncodeGeneralStatus(message_router_response, outgoing_message);
+      EncodeExtendedStatus(message_router_response, outgoing_message);
+      EncodeMessageRouterResponseData(message_router_response,
+                                      outgoing_message);
     } else { /* connected IO Message to send */
-      message_size = EncodeDataItemType(common_packet_format_data_item,
-                                        &message, message_size);
+      EncodeDataItemType(common_packet_format_data_item,
+                         outgoing_message);
 
-      message_size = EncodeDataItemLength(common_packet_format_data_item,
-                                          &message, message_size);
+      EncodeDataItemLength(common_packet_format_data_item,
+                           outgoing_message);
 
-      message_size = EncodeDataItemData(common_packet_format_data_item,
-                                        &message, message_size);
+      EncodeDataItemData(common_packet_format_data_item,
+                         outgoing_message);
     }
   }
 
@@ -691,30 +736,30 @@ int AssembleLinearMessage(
     for (int j = 0; j < 2; j++) {
       if (common_packet_format_data_item->address_info_item[j].type_id
           == type) {
-        message_size = EncodeSockaddrInfoItemTypeId(
-          message_size, j, common_packet_format_data_item, &message);
+        EncodeSockaddrInfoItemTypeId(
+          j, common_packet_format_data_item, outgoing_message);
 
-        message_size = EncodeSockaddrInfoLength(message_size, j,
-                                                common_packet_format_data_item,
-                                                &message);
+        EncodeSockaddrInfoLength(j,common_packet_format_data_item,
+                                 outgoing_message);
 
-        message_size += EncapsulateIpAddress(
+        outgoing_message->used_message_length += EncapsulateIpAddress(
           common_packet_format_data_item->address_info_item[j].sin_port,
           common_packet_format_data_item->address_info_item[j].sin_addr,
-          &message);
+          &outgoing_message->current_message_position);
 
-        message_size += FillNextNMessageOctetsWithValueAndMoveToNextPosition(
-          0, 8, &message);
+        outgoing_message->used_message_length +=
+          FillNextNMessageOctetsWithValueAndMoveToNextPosition(
+            0, 8, &outgoing_message->current_message_position);
         break;
       }
     }
   }
-  return message_size;
+  return outgoing_message->used_message_length;
 }
 
-int AssembleIOMessage(CipCommonPacketFormatData *common_packet_format_data_item,
-                      EipUint8 *message) {
+int AssembleIOMessage(
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message) {
   return AssembleLinearMessage(0, common_packet_format_data_item,
-                               &g_message_data_reply_buffer[0]);
+                               outgoing_message);
 }
-

+ 31 - 27
source/src/enet_encap/cpf.h

@@ -12,12 +12,12 @@
 
 /** @ingroup ENCAP
  * @brief CPF is Common Packet Format
- * CPF packet := <number of items> {<items>}
- * item := <TypeID> <Length> <data>
- * <number of items> := two bytes
- * <TypeID> := two bytes
- * <Length> := two bytes
- * <data> := <the number of bytes specified by Length>
+ * CPF packet := \<number of items\> {\<items\>}
+ * item := \<TypeID\> \<Length\> \<data\>
+ * \<number of items\> := two bytes
+ * \<TypeID\> := two bytes
+ * \<Length\> := two bytes
+ * \<data\> := \<the number of bytes specified by Length\>
  */
 
 /** @brief Definition of Item ID numbers used for address and data items in CPF structures */
@@ -75,26 +75,29 @@ typedef struct {
  * Parse the CPF data from a received unconnected explicit message and
  * hand the data on to the message router
  *
- * @param  received_data pointer to the encapsulation structure with the received message
- * @param  reply_buffer reply buffer
+ * @param received_data pointer to the encapsulation structure with the received message
+ * @param originator_address Address struct of the originator
+ * @param outgoing_message The outgoing ENIP message struct
  * @return number of bytes to be sent back. < 0 if nothing should be sent
  */
 int NotifyCommonPacketFormat(EncapsulationData *const received_data,
-                             EipUint8 *const reply_buffer,
-                             struct sockaddr *originator_address);
+                             const struct sockaddr *const originator_address,
+                             ENIPMessage *const outgoing_message);
 
 /** @ingroup ENCAP
  * Parse the CPF data from a received connected explicit message, check
  * the connection status, update any timers, and hand the data on to
  * the message router
  *
- * @param  received_data pointer to the encapsulation structure with the received message
- * @param  reply_buffer reply buffer
+ * @param received_data pointer to the encapsulation structure with the received message
+ * @param originator_address Address struct of the originator
+ * @param outgoing_message The outgoing ENIP message struct
  * @return number of bytes to be sent back. < 0 if nothing should be sent
  */
-int NotifyConnectedCommonPacketFormat(EncapsulationData *received_data,
-                                      EipUint8 *reply_buffer,
-                                      struct sockaddr *originator_address);
+int NotifyConnectedCommonPacketFormat(
+  const EncapsulationData *const received_data,
+  const struct sockaddr *const originator_address,
+  ENIPMessage *const outgoing_message);
 
 /** @ingroup ENCAP
  *  Create CPF structure out of the received data.
@@ -112,29 +115,30 @@ EipStatus CreateCommonPacketFormatStructure(
 
 /** @ingroup ENCAP
  * Copy data from CPFDataItem into linear memory in message for transmission over in encapsulation.
- * @param  message_router_response  pointer to message router response which has to be aligned into linear memory.
  * @param  common_packet_format_data_item pointer to CPF structure which has to be aligned into linear memory.
- * @param  message    pointer to linear memory.
- * @return length of reply in pa_msg in bytes
+ * @param  message Modified ENIP message struct
+ * @return length of modification in bytes
  *     EIP_ERROR .. error
  */
 int AssembleIOMessage(
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 *message);
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const message);
 
 
 /** @ingroup ENCAP
- * Copy data from MRResponse struct and CPFDataItem into linear memory in message for transmission over in encapsulation.
+ * @brief Copy data from message_router_response struct and common_packet_format_data_item into
+ * ENIPMessage struct outgoing_message via encapsulation.
+ *
  * @param  message_router_response	pointer to message router response which has to be aligned into linear memory.
  * @param  common_packet_format_data_item	pointer to CPF structure which has to be aligned into linear memory.
- * @param  message		pointer to linear memory.
- * @return length of reply in pa_msg in bytes
- *         EIP_ERROR .. error
+ * @param  outgoing_message Modified ENIP message struct
+ * @return length of modification in bytes
+ *         kEipStatusError .. error
  */
 int AssembleLinearMessage(
-  CipMessageRouterResponse *message_router_response,
-  CipCommonPacketFormatData *common_packet_format_data_item,
-  EipUint8 *message);
+  const CipMessageRouterResponse *const message_router_response,
+  const CipCommonPacketFormatData *const common_packet_format_data_item,
+  ENIPMessage *const outgoing_message);
 
 /** @ingroup ENCAP
  * @brief Data storage for the any CPF data

Plik diff jest za duży
+ 410 - 277
source/src/enet_encap/encap.c


+ 38 - 2
source/src/enet_encap/encap.h

@@ -45,8 +45,8 @@ typedef struct encapsulation_data {
   CipUdint status;
   CipOctet sender_context[8]; /**< length of 8, according to the specification */
   CipUdint options;
-  EipUint8 *communication_buffer_start; /**< Pointer to the communication buffer used for this message */
-  EipUint8 *current_communication_buffer_position; /**< The current position in the communication buffer during the decoding process */
+  const EipUint8 *communication_buffer_start; /**< Pointer to the communication buffer used for this message */
+  const EipUint8 *current_communication_buffer_position; /**< The current position in the communication buffer during the decoding process */
 } EncapsulationData;
 
 typedef struct encapsulation_interface_information {
@@ -93,4 +93,40 @@ void CloseEncapsulationSessionBySockAddr(
 
 void CloseClass3ConnectionBasedOnSession(size_t encapsulation_session_handle);
 
+/* No reason to use this functions outside the encapsulation layer, they are here for testing */
+typedef struct enip_message ENIPMessage;
+
+void EncapsulateListIdentityResponseMessage(
+  const EncapsulationData *const receive_data,
+  ENIPMessage *const outgoing_message);
+
+EipInt16 CreateEncapsulationStructure(const EipUint8 *receive_buffer,
+                                      int receive_buffer_length,
+                                      EncapsulationData *const encapsulation_data);
+
+void SkipEncapsulationHeader(ENIPMessage *const outgoing_message);
+
+void GenerateEncapsulationHeader(const EncapsulationData *const receive_data,
+                                 const size_t command_specific_data_length,
+                                 const size_t session_handle,
+                                 const EncapsulationProtocolErrorCode encapsulation_protocol_status,
+                                 ENIPMessage *const outgoing_message);
+
+void HandleReceivedListServicesCommand(
+  const EncapsulationData *const receive_data,
+  ENIPMessage *const outgoing_message);
+
+void HandleReceivedListInterfacesCommand(
+  const EncapsulationData *const receive_data,
+  ENIPMessage *const outgoing_message);
+
+void HandleReceivedRegisterSessionCommand(int socket,
+                                          const EncapsulationData *const receive_data,
+                                          ENIPMessage *const outgoing_message);
+
+EipStatus HandleReceivedSendRequestResponseDataCommand(
+  const EncapsulationData *const receive_data,
+  const struct sockaddr *const originator_address,
+  ENIPMessage *const outgoing_message);
+
 #endif /* OPENER_ENCAP_H_ */

+ 3 - 2
source/src/enet_encap/endianconv.c

@@ -255,9 +255,10 @@ int GetEndianess() {
   return g_opener_platform_endianess;
 }
 
-void MoveMessageNOctets(int amount_of_bytes_moved,
-                        const CipOctet **message_runner) {
+int MoveMessageNOctets(const int amount_of_bytes_moved,
+                       const CipOctet **message_runner) {
   (*message_runner) += amount_of_bytes_moved;
+  return amount_of_bytes_moved;
 }
 
 int FillNextNMessageOctetsWith(CipOctet value,

+ 3 - 3
source/src/enet_encap/endianconv.h

@@ -105,7 +105,7 @@ int AddLintToMessage(const EipUint64 pa_unData,
  *
  * @param port Port of the socket, has to be provided in big-endian
  * @param address IP address of the socket, has to be provided in big-endian
- * @param communcation_buffer The message buffer for sending the message
+ * @param communication_buffer The message buffer for sending the message
  */
 int EncapsulateIpAddress(EipUint16 port,
                          EipUint32 address,
@@ -124,8 +124,8 @@ void DetermineEndianess(void);
  */
 int GetEndianess(void);
 
-void MoveMessageNOctets(int n,
-                        const CipOctet **message_runner);
+int MoveMessageNOctets(const int n,
+                       const CipOctet **message_runner);
 
 int FillNextNMessageOctetsWith(CipOctet value,
                                unsigned int n,

+ 61 - 57
source/src/opener_api.h

@@ -44,14 +44,12 @@ void ConfigureMacAddress(const char *network_interface);
  * @brief Configure the domain name of the device
  * @param domain_name the domain name to be used
  */
-void ConfigureDomainName(
-  );
+void ConfigureDomainName(void);
 /** @ingroup CIP_API
  * @brief Configure the host name of the device
  * @param host_name the host name to be used
  */
-void ConfigureHostName(
-  );
+void ConfigureHostName(void);
 
 /** @ingroup CIP_API
  * @brief Set the serial number of the device's identity object.
@@ -121,6 +119,7 @@ CipInstance *GetCipInstance(const CipClass *RESTRICT const cip_object,
 CipAttributeStruct *GetCipAttribute(const CipInstance *const cip_instance,
                                     const EipUint16 attribute_number);
 
+typedef void (*InitializeCipClass)(CipClass *); /**< Initializer function for CIP class initialization */
 /** @ingroup CIP_API
  * @brief Allocate memory for new CIP Class and attributes
  *
@@ -129,21 +128,15 @@ CipAttributeStruct *GetCipAttribute(const CipInstance *const cip_instance,
  *
  *  @param class_id class ID of the new class
  *  @param number_of_class_attributes number of class attributes
- *  @param get_attribute_all_mask mask of which attributes are included in the
- *  class getAttributeAll.
- *  If the mask is 0 the getAttributeAll service will not be added as class
- *  service
+ *  @param highest_class_attribute_number Highest attribute number from the set of implemented class attributes
  *  @param number_of_class_services number of class services
- *  @param number_of_instance_attributes number of attributes of each instance
- *  @param instance_attributes_get_attributes_all_mask  mask of which attributes
- *  are included in the instance getAttributeAll
- *  If the mask is 0 the getAttributeAll service will not be added as class
- *  service
+ *  @param number_of_instance_attributes Number of implemented instance attributes
+ *  @param highest_instance_attribute_number Highest attribute number from the set of implemented instance attributes
  *  @param number_of_instance_services number of instance services
  *  @param number_of_instances number of initial instances to create
- *  @param class_name  class name (for debugging class structure)
- *  @param class_revision class revision
- *  @param (*InitializeCipClass)(CipClass*) For non-standard implementation of
+ *  @param name class name (for debugging class structure)
+ *  @param revision class revision
+ *  @param initializer For non-standard implementation of
  *  class attributes, function realizes specific implementation
  *  @return pointer to new class object
  *      0 on error
@@ -159,7 +152,8 @@ CipClass *CreateCipClass( const EipUint32 class_id,
                           const int number_of_instances,
                           char *name,
                           const EipUint16 revision,
-                          void (*InitializeCipClass)(CipClass *) );
+                          InitializeCipClass initializer );
+
 /** @ingroup CIP_API
  * @brief Add a number of CIP instances to a given CIP class
  *
@@ -186,8 +180,8 @@ CipInstance *AddCipInstances(
  * @brief Create one instance of a given class with a certain instance number
  *
  * This function can be used for creating out of order instance numbers
- * @param pa_pstCIPClass the class the instance should be created for
- * @param pa_nInstanceId the instance id of the created instance
+ * @param cip_class_to_add_instance the class the instance should be created for
+ * @param instance_id the instance id of the created instance
  * @return pointer to the created instance, if an instance with the given id
  *         already exists the existing is returned an no new instance is created
  *
@@ -202,11 +196,11 @@ CipInstance *AddCIPInstance(CipClass *RESTRICT const cip_class_to_add_instance,
  *  the attributes array is not expandable if you insert an attributes that has
  *  already been defined, the previous attributes will be replaced
  *
- *  @param pa_pInstance pointer to CIP class. (may be also instance 0)
- *  @param pa_nAttributeNr number of attribute to be inserted.
- *  @param cip_data_type type of attribute to be inserted.
- *  @param cip_data pointer to data of attribute.
- *  @param cip_flags flags to indicate set-ability and get-ability of attribute.
+ *  @param cip_instance Pointer to CIP class instance (Instance 0)
+ *  @param attribute_number Number of attribute to be inserted.
+ *  @param cip_data_type Type of attribute to be inserted.
+ *  @param cip_data Pointer to data of attribute.
+ *  @param cip_flags Flags to indicate set-ability and get-ability of attribute.
  */
 
 
@@ -277,9 +271,9 @@ int EncodeData(const EipUint8 cip_data_type,
  *  @return length of taken bytes
  *          -1 .. error
  */
-int DecodeData(const EipUint8 cip_type,
-               void *const data,
-               const EipUint8 **const message);
+int DecodeData(const EipUint8 cip_data_type,
+               void *const cip_data,
+               const EipUint8 **const cip_message);
 
 /** @ingroup CIP_API
  * @brief Create an instance of an assembly object
@@ -432,19 +426,22 @@ void ConfigureListenOnlyConnectionPoint(
  * @brief Notify the encapsulation layer that an explicit message has been
  * received via TCP.
  *
- * @param socket_handle socket handle from which data is received.
- * @param buffer buffer that contains the received data. This buffer will also
+ * @param socket_handle Socket handle from which data is received.
+ * @param buffer Buffer that contains the received data. This buffer will also
  * contain the response if one is to be sent.
- * @param buffer length of the data in buffer.
- * @param number_of_remaining_bytes return how many bytes of the input are left
+ * @param length Length of the data in buffer.
+ * @param number_of_remaining_bytes Return how many bytes of the input are left
  * over after we're done here
+ * @param originator_address Address struct of the message originator
+ * @param outgoing_message The outgoing ENIP message
  * @return length of reply that need to be sent back
  */
-int HandleReceivedExplictTcpData(int socket,
+int HandleReceivedExplictTcpData(int socket_handle,
                                  EipUint8 *buffer,
-                                 size_t buffer_length,
+                                 size_t length,
                                  int *number_of_remaining_bytes,
-                                 struct sockaddr *originator_address);
+                                 struct sockaddr *originator_address,
+                                 ENIPMessage *const outgoing_message);
 
 /** @ingroup CIP_API
  * @brief Notify the encapsulation layer that an explicit message has been
@@ -457,14 +454,17 @@ int HandleReceivedExplictTcpData(int socket,
  * @param buffer_length length of the data in buffer.
  * @param number_of_remaining_bytes return how many bytes of the input are left
  * over after we're done here
+ * @param unicast Was the data receieved from a multicast address
+ * @param outgoing_message Outgoing ENIP message
  * @return length of reply that need to be sent back
  */
-int HandleReceivedExplictUdpData(int socket,
-                                 struct sockaddr_in *from_address,
-                                 EipUint8 *buffer,
-                                 size_t buffer_length,
+int HandleReceivedExplictUdpData(const int socket_handle,
+                                 const struct sockaddr_in *from_address,
+                                 const EipUint8 *buffer,
+                                 const size_t buffer_length,
                                  int *number_of_remaining_bytes,
-                                 bool unicast);
+                                 bool unicast,
+                                 ENIPMessage *const outgoing_message);
 
 /** @ingroup CIP_API
  *  @brief Notify the connection manager that data for a connection has been
@@ -479,7 +479,7 @@ int HandleReceivedExplictUdpData(int socket,
  *  @return EIP_OK on success
  */
 EipStatus
-HandleReceivedConnectedData(EipUint8 *received_data,
+HandleReceivedConnectedData(const EipUint8 *const received_data,
                             int received_data_length,
                             struct sockaddr_in *from_address);
 
@@ -488,8 +488,11 @@ HandleReceivedConnectedData(EipUint8 *received_data,
  * WatchdogTimeout) have timed out.
  *
  * If the a timeout occurs the function performs the necessary action. This
- * function should be called periodically once every OPENER_TIMER_TICK
- * milliseconds.
+ * function should be called periodically once every @ref kOpenerTimerTickInMilliSeconds
+ * milliseconds. In order to simplify the algorithm if more time was lapsed, the elapsed
+ * time since the last call of the function is given as a parameter.
+ *
+ * @param elapsed_time Elapsed time in milliseconds since the last call of ManageConnections
  *
  * @return EIP_OK on success
  */
@@ -527,7 +530,7 @@ TriggerConnections(unsigned int output_assembly_id,
  * the encapsulation layer.
  * @param socket_handle the handler to the socket of the closed connection
  */
-void CloseSession(int socket);
+void CloseSession(int socket_handle);
 
 /**  @defgroup CIP_CALLBACK_API Callback Functions Demanded by OpENer
  * @ingroup CIP_API
@@ -662,7 +665,7 @@ void RunIdleChanged(EipUint32 run_idle_value);
 /** @ingroup CIP_CALLBACK_API
  * @brief create a producing or consuming UDP socket
  *
- * @param communication_direction PRODCUER or CONSUMER
+ * @param communication_direction kUdpCommunicationDirectionProducing or kUdpCommunicationDirectionConsuming
  * @param socket_data pointer to the address holding structure
  *     Attention: For producing point-to-point connection the
  *     *pa_pstAddr->sin_addr.s_addr member is set to 0 by OpENer. The network
@@ -672,6 +675,7 @@ void RunIdleChanged(EipUint32 run_idle_value);
  * pa_pstAddr->sin_addr.s_addr to the correct address of the originator.
  * FIXME add an additional parameter that can be used by the CIP stack to
  * request the originators sockaddr_in data.
+ * @param qos_for_socket CIP QoS object parameter value
  * @return socket identifier on success
  *         -1 on error
  */
@@ -680,17 +684,17 @@ int CreateUdpSocket(UdpCommuncationDirection communication_direction,
                     CipUsint qos_for_socket);
 
 /** @ingroup CIP_CALLBACK_API
- * @brief create a producing or consuming UDP socket
+ * @brief Create a producing or consuming UDP socket
  *
- * @param socket_data pointer to the "send to" address
- * @param socket_handle socket descriptor to send on
- * @param data pointer to the data to send
- * @param data_length length of the data to send
- * @return  EIP_SUCCESS on success
+ * @param socket_data Pointer to the "send to" address
+ * @param socket_handle Socket descriptor to send on
+ * @param data Pointer to the data to send
+ * @param data_length Length of the data to send
+ * @return kEipStatusOk on success
  */
 EipStatus
 SendUdpData(struct sockaddr_in *socket_data,
-            int socket,
+            int socket_handle,
             EipUint8 *data,
             EipUint16 data_length);
 
@@ -699,7 +703,7 @@ SendUdpData(struct sockaddr_in *socket_data,
  *
  * @param socket_handle socket descriptor to close
  */
-void CloseSocket(const int socket);
+void CloseSocket(const int socket_handle);
 
 /** @mainpage OpENer - Open Source EtherNet/IP(TM) Communication Stack
  * Documentation
@@ -757,11 +761,11 @@ void CloseSocket(const int socket);
  * @section gen_config_section General Stack Configuration
  * The general stack properties have to be defined prior to building your
  * production. This is done by providing a file called opener_user_conf.h. An
- * example file can be found in the src/ports/platform-pc directory. The
- * documentation of the example file for the necessary configuration options:
+ * example file can be found in the src/ports/POSIX or src/ports/WIN32 directory.
+ * The documentation of the example file for the necessary configuration options:
  * opener_user_conf.h
  *
- * @copydoc opener_user_conf.h
+ * @copydoc ./ports/POSIX/sample_application/opener_user_conf.h
  *
  * @section startup_sec Startup Sequence
  * During startup of your EtherNet/IP(TM) device the following steps have to be
@@ -841,7 +845,7 @@ void CloseSocket(const int socket);
  *      .
  *   - Cyclically update the connection status:\n
  *     In order that OpENer can determine when to produce new data on
- *     connections or that a connection timed out every @ref OPENER_TIMER_TICK
+ *     connections or that a connection timed out every @ref kOpenerTimerTickInMilliSeconds
  * milliseconds the
  *     function EIP_STATUS ManageConnections(void) has to be called.
  *
@@ -878,7 +882,7 @@ void CloseSocket(const int socket);
  * guarding conditions for using OpENer in own products. For this please look
  * in license text as shown below:
  *
- * @include "license.txt"
+ * @include "../license.txt"
  *
  */
 

+ 1 - 1
source/src/ports/CMakeLists.txt

@@ -14,6 +14,6 @@ opener_common_includes()
 #######################################
 opener_platform_support("INCLUDES")
 
-set( PLATFORM_GENERIC_SRC generic_networkhandler.c socket_timer.c )
+set( PLATFORM_GENERIC_SRC generic_networkhandler.c socket_timer.c  udp_protocol.c)
 
 add_library( PLATFORM_GENERIC ${PLATFORM_GENERIC_SRC})

+ 21 - 0
source/src/ports/MINGW/CMakeLists.txt

@@ -0,0 +1,21 @@
+add_subdirectory(sample_application)
+
+set( PLATFORM_SPEC_SRC networkhandler.c opener_error.c networkconfig.c)
+
+#######################################
+# Add common includes                 #
+#######################################
+opener_common_includes()
+
+#######################################
+# Add platform specific things        #
+#######################################
+opener_platform_support("INCLUDES")
+
+set (PLATFORMLIBNAME ${OpENer_PLATFORM}PLATFORM)
+
+add_library( ${PLATFORMLIBNAME} ${PLATFORM_SPEC_SRC}) 
+
+add_executable(OpENer main.c)
+
+target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils  SAMPLE_APP ENET_ENCAP ws2_32 IPHLPAPI ${OpENer_CIP_OBJECTS} )

+ 93 - 0
source/src/ports/MINGW/main.c

@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include "generic_networkhandler.h"
+#include "opener_api.h"
+#include "cipcommon.h"
+#include "trace.h"
+#include "networkconfig.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+extern int newfd;
+
+/******************************************************************************/
+/*!\brief Signal handler function for ending stack execution
+ *
+ * @param pa_nSig the signal we received
+ */
+void
+LeaveStack(int pa_nSig);
+
+/*****************************************************************************/
+/*! \brief Flag indicating if the stack should end its execution
+ */
+int g_end_stack = 0;
+
+/******************************************************************************/
+int main(int argc,
+         char *arg[]) {
+  EipUint16 nUniqueConnectionID;
+
+  if (argc != 2) {
+    printf("Wrong number of command line parameters!\n");
+    printf("The correct command line parameters are:\n");
+    printf(
+      "./OpENer ip-address\n");
+    printf(
+      "    e.g. ./OpENer 192.168.259.22\n");
+    exit(0);
+  } else {
+    DoublyLinkedListInitialize(&connection_list,
+                               CipConnectionObjectListArrayAllocator,
+                               CipConnectionObjectListArrayFree);
+    /* fetch Internet address info from the platform */
+    ConfigureDomainName();
+    ConfigureHostName();
+    ConfigureNetworkInterface(arg[1] );
+  }
+
+  /*for a real device the serial number should be unique per device */
+  SetDeviceSerialNumber(123456789);
+
+  /* nUniqueConnectionID should be sufficiently random or incremented and stored
+   *  in non-volatile memory each time the device boots.
+   */
+  nUniqueConnectionID = rand();
+
+  /* Setup the CIP Layer */
+  CipStackInit(nUniqueConnectionID);
+
+  /* Setup Network Handles */
+  if ( kEipStatusOk == NetworkHandlerInitialize() ) {
+    g_end_stack = 0;
+#ifndef WIN32
+    /* register for closing signals so that we can trigger the stack to end */
+    signal(SIGHUP, LeaveStack);
+#endif
+
+    /* The event loop. Put other processing you need done continually in here */
+    while (1 != g_end_stack) {
+      if ( kEipStatusOk != NetworkHandlerProcessOnce() ) {
+        break;
+      }
+    }
+
+    /* clean up network state */
+    NetworkHandlerFinish();
+  }
+  /* close remaining sessions and connections, cleanup used data */
+  ShutdownCipStack();
+
+  return -1;
+}
+
+void LeaveStack(int pa_nSig) {
+  (void) pa_nSig; /* kill unused parameter warning */
+  OPENER_TRACE_STATE("got signal HUP\n");
+  g_end_stack = 1;
+}

+ 257 - 0
source/src/ports/MINGW/networkconfig.c

@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#define WIN32_LEAN_AND_MEAN
+#include "ciptcpipinterface.h"
+#include "networkconfig.h"
+#include "cipcommon.h"
+#include "ciperror.h"
+#include "opener_api.h"
+#include "trace.h"
+#include "cipethernetlink.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <winsock2.h>
+#include <windows.h>
+#include <Ws2tcpip.h>
+#include <iphlpapi.h>
+
+#define WORKING_BUFFER_SIZE 15000
+#define MAX_TRIES 3
+
+
+EipStatus ConfigureNetworkInterface(const char *const network_interface) {
+
+  PIP_ADAPTER_INFO pAdapterInfo;
+  PIP_ADAPTER_INFO pAdapter = NULL;
+  CipDword dwRetVal = 0;
+
+  CipUdint ulOutBufLen = sizeof(IP_ADAPTER_INFO);
+  pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(1,sizeof(IP_ADAPTER_INFO) );
+  if (pAdapterInfo == NULL) {
+    printf("Error allocating memory needed to call GetAdaptersinfo\n");
+    return kEipStatusError;
+  }
+  // Make an initial call to GetAdaptersInfo to get
+  // the necessary size into the ulOutBufLen variable
+  if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
+    CipFree(pAdapterInfo);
+    pAdapterInfo = (IP_ADAPTER_INFO *)CipCalloc(ulOutBufLen,sizeof(CipUdint) );
+    if (pAdapterInfo == NULL) {
+      printf("Error allocating memory needed to call GetAdaptersinfo\n");
+      return kEipStatusError;
+    }
+  }
+
+
+  if ( (dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) ) == NO_ERROR ) {
+    pAdapter = pAdapterInfo;
+    while (pAdapter) {
+      if (strcmp( pAdapter->IpAddressList.IpAddress.String,
+                  network_interface) == 0) {
+        for (int i = 0; i < 6; i++) {
+          memcpy(&g_ethernet_link.physical_address, pAdapter->Address,
+                 6 * sizeof(CipUsint) );
+        }
+
+        interface_configuration_.ip_address = inet_addr(
+          pAdapter->IpAddressList.IpAddress.String);
+        interface_configuration_.network_mask = inet_addr(
+          pAdapter->IpAddressList.IpMask.String);
+        interface_configuration_.gateway = inet_addr(
+          pAdapter->GatewayList.IpAddress.String);
+
+        CipUdint host_id = ntohl(interface_configuration_.ip_address)
+                           & ~ntohl(interface_configuration_.network_mask);              /* see CIP spec 3-5.3 for multicast address algorithm*/
+        host_id -= 1;
+        host_id &= 0x3ff;
+
+        g_multicast_configuration.starting_multicast_address = htonl(
+          ntohl(inet_addr("239.192.1.0") ) + (host_id << 5) );
+      }
+      pAdapter = pAdapter->Next;
+    }
+  }
+  else {
+    printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);
+
+  }
+  CipFree(pAdapterInfo);
+  CipFree(pAdapter);
+  return kEipStatusOk;
+}
+
+void ConfigureDomainName() {
+  // This was a parameter!
+  int interface_index = 0;
+
+  CipDword dwSize = 0;
+  int i = 0;
+  // Set the flags to pass to GetAdaptersAddresses
+  CipUdint flags = GAA_FLAG_INCLUDE_PREFIX;
+  CipDword dwRetVal = 0;
+  // default to unspecified address family (both)
+  CipUdint family = AF_UNSPEC;
+
+  LPVOID lpMsgBuf = NULL;
+
+  PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+  PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
+  IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
+  CipUdint outBufLen = 0;
+  CipUdint tries = 0;
+
+  family = AF_INET;
+  // Allocate a 15 KB buffer to start with.
+  outBufLen = WORKING_BUFFER_SIZE;
+
+  do {
+
+    pAddresses = (IP_ADAPTER_ADDRESSES *)CipCalloc(1,outBufLen);
+    if (pAddresses == NULL) {
+      printf
+        ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
+      exit(1);
+    }
+
+    dwRetVal =
+      GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
+
+    if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
+      CipFree(pAddresses);
+      pAddresses = NULL;
+    }
+    else {
+      break;
+    }
+
+    tries++;
+
+  } while ( (dwRetVal == ERROR_BUFFER_OVERFLOW) && (tries < MAX_TRIES) );
+
+  if (dwRetVal == NO_ERROR) {
+    // If successful, output some information from the data we received
+    pCurrAddresses = pAddresses;
+    while (pCurrAddresses) {
+      if (interface_index == pCurrAddresses->IfIndex) {
+        pDnServer = pCurrAddresses->FirstDnsServerAddress;
+        if (pDnServer) {
+          for (i = 0; pDnServer != NULL; i++) {
+            pDnServer = pDnServer->Next;
+          }
+        }
+
+        char pStringBuf[INET_ADDRSTRLEN];
+        if (i != 0) {
+
+          if (NULL != interface_configuration_.domain_name.string) {
+            /* if the string is already set to a value we have to free the resources
+             * before we can set the new value in order to avoid memory leaks.
+             */
+            CipFree(interface_configuration_.domain_name.string);
+          }
+          interface_configuration_.domain_name.length = strlen(
+            pCurrAddresses->DnsSuffix);
+          if (interface_configuration_.domain_name.length) {
+            interface_configuration_.domain_name.string = (CipByte *)CipCalloc(
+              interface_configuration_.domain_name.length + 1,
+              sizeof(CipUsint) );
+            strcpy(interface_configuration_.domain_name.string,
+                   pCurrAddresses->DnsSuffix);
+          }
+          else {
+            interface_configuration_.domain_name.string = NULL;
+          }
+/*
+          inet_ntop(AF_INET,
+                   pCurrAddresses->FirstDnsServerAddress->Address.lpSockaddr->sa_data + 2,
+                   interface_configuration_.name_server,
+                   sizeof(interface_configuration_.name_server) );
+          inet_ntop(AF_INET,
+                   pCurrAddresses->FirstDnsServerAddress->Next->Address.lpSockaddr->sa_data + 2,
+                   interface_configuration_.name_server_2,
+                   sizeof(interface_configuration_.name_server_2) );
+ */
+        }
+        else{ interface_configuration_.domain_name.length = 0;}
+
+      }
+      pCurrAddresses = pCurrAddresses->Next;
+    }
+  }
+  else {
+    OPENER_TRACE_INFO("Call to GetAdaptersAddresses failed with error: %d\n",
+                      dwRetVal);
+    if (dwRetVal == ERROR_NO_DATA) {
+      OPENER_TRACE_INFO(
+        "\tNo addresses were found for the requested parameters\n");
+    }
+    else {
+
+      if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                        FORMAT_MESSAGE_FROM_SYSTEM |
+                        FORMAT_MESSAGE_IGNORE_INSERTS,
+                        NULL, dwRetVal,
+                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                        // Default language
+                        (LPTSTR)&lpMsgBuf, 0, NULL) ) {
+        OPENER_TRACE_INFO("\tError: %s", lpMsgBuf);
+        CipFree(lpMsgBuf);
+        if (pAddresses) {
+          CipFree(pAddresses);
+        }
+        exit(1);
+      }
+    }
+  }
+
+  if (pAddresses) {
+    CipFree(pAddresses);
+  }
+
+
+}
+
+void ConfigureHostName(void) {
+  CipWord wVersionRequested;
+  WSADATA wsaData;
+  int err;
+
+  /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
+  wVersionRequested = MAKEWORD(2, 2);
+
+  err = WSAStartup(wVersionRequested, &wsaData);
+  if (err != 0) {
+    /* Tell the user that we could not find a usable */
+    /* Winsock DLL.                                  */
+    printf("WSAStartup failed with error: %d\n", err);
+    return;
+  }
+
+  char hostname[256] = "";
+  gethostname(hostname, sizeof(hostname) );
+
+  //WSACleanup();
+
+
+
+
+  if (NULL != hostname_.string) {
+    /* if the string is already set to a value we have to free the resources
+     * before we can set the new value in order to avoid memory leaks.
+     */
+    CipFree(hostname_.string);
+  }
+  hostname_.length = strlen(hostname);
+  if (hostname_.length) {
+    hostname_.string = (CipByte *) CipCalloc( hostname_.length + 1,
+                                              sizeof(CipByte) );
+    strcpy(hostname_.string, hostname);
+  } else {
+    hostname_.string = NULL;
+  }
+}

+ 0 - 0
source/src/ports/MINGW/networkconfig.h


+ 53 - 0
source/src/ports/MINGW/networkhandler.c

@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#define WIN32_LEAN_AND_MEAN
+#include <string.h>
+#include <stdlib.h>
+#include <winsock2.h>
+#include <windows.h>
+#include <Ws2tcpip.h>
+
+#include "networkhandler.h"
+
+#include "generic_networkhandler.h"
+
+MicroSeconds getMicroSeconds() {
+  LARGE_INTEGER performance_counter;
+  LARGE_INTEGER performance_frequency;
+
+  QueryPerformanceCounter(&performance_counter);
+  QueryPerformanceFrequency(&performance_frequency);
+
+  return (MicroSeconds) (performance_counter.QuadPart * 1000000LL
+                         / performance_frequency.QuadPart);
+}
+
+MilliSeconds GetMilliSeconds(void) {
+  return (MilliSeconds) (getMicroSeconds() / 1000ULL);
+}
+
+EipStatus NetworkHandlerInitializePlatform(void) {
+  WSADATA wsaData;
+  const WORD wVersionRequested = MAKEWORD(2, 2);
+  WSAStartup(wVersionRequested, &wsaData);
+
+  return kEipStatusOk;
+}
+
+void CloseSocketPlatform(int socket_handle) {
+  closesocket(socket_handle);
+}
+
+int SetSocketToNonBlocking(int socket_handle) {
+  u_long iMode = 1;
+  return ioctlsocket(socket_handle, FIONBIO, &iMode);
+}
+
+int SetQosOnSocket(int socket,
+                   CipUsint qos_value) {
+  CipUsint set_tos = qos_value;
+  return setsockopt(socket, IPPROTO_IP, IP_TOS, &set_tos, sizeof(set_tos) );
+}

+ 43 - 0
source/src/ports/MINGW/networkhandler.h

@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#ifndef NETWORKHANDLER_H_
+#define NETWORKHANDLER_H_
+
+#include "winsock2.h"
+#include "typedefs.h"
+#include <Ws2tcpip.h>
+
+#define OPENER_SOCKET_WOULD_BLOCK WSAEWOULDBLOCK
+
+// THis type is defined in Ws2tcpip.h!
+//typedef unsigned long socklen_t;
+
+EipStatus NetworkHandlerInitializePlatform(void);
+
+/** @brief Platform dependent code to close a socket
+ *
+ *  @param socket_handle The socket handle to be closed
+ */
+void CloseSocketPlatform(int socket_handle);
+
+int SetSocketToNonBlocking(int socket_handle);
+
+/** @brief This function shall return the current time in microseconds relative to epoch, and shall be implemented in a port specific networkhandler
+ *
+ *  @return Current time relative to epoch as MicroSeconds
+ */
+MicroSeconds GetMicroSeconds(void);
+
+/** @brief This function shall return the current time in milliseconds relative to epoch, and shall be implemented in a port specific networkhandler
+ *
+ *  @return Current time relative to epoch as MilliSeconds
+ */
+MilliSeconds GetMilliSeconds(void);
+
+int SetQosOnSocket(int socket,
+                   CipUsint qos_value);
+
+#endif /*NETWORKHANDLER_H_*/

+ 36 - 0
source/src/ports/MINGW/opener_error.c

@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+/** @file opener_error.c
+ *  @author Martin Melik Merkumians
+ *  @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError
+ *
+ */
+
+ #include <windows.h>
+
+ #include "opener_error.h"
+
+int GetSocketErrorNumber() {
+  return WSAGetLastError();
+}
+
+char *GetErrorMessage(int error_number) {
+  char *error_message = NULL;
+  FormatMessage(
+    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+    NULL,
+    error_number,
+    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+    &error_message,
+    0,
+    NULL);
+  return error_message;
+}
+
+void FreeErrorMessage(char *error_message) {
+  LocalFree(error_message);
+}

+ 14 - 0
source/src/ports/MINGW/sample_application/CMakeLists.txt

@@ -0,0 +1,14 @@
+#######################################
+# Add common includes                 #
+#######################################
+opener_common_includes()
+
+#######################################
+# Add platform specific things        #
+#######################################
+
+opener_platform_support("INCLUDES")
+
+opener_platform_support("INCLUDES")
+
+add_library(SAMPLE_APP sampleapplication.c)

+ 153 - 0
source/src/ports/MINGW/sample_application/opener_user_conf.h

@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#ifndef OPENER_USER_CONF_H_
+#define OPENER_USER_CONF_H_
+
+/** @file
+ * @brief OpENer configuration setup
+ *
+ * This file contains the general application specific configuration for OpENer.
+ *
+ * Furthermore you have to specific platform specific network include files.
+ * OpENer needs definitions for the following data-types
+ * and functions:
+ *    - struct sockaddr_in
+ *    - AF_INET
+ *    - INADDR_ANY
+ *    - htons
+ *    - ntohl
+ *    - inet_addr
+ */
+
+ #include "typedefs.h"
+
+typedef unsigned short in_port_t;
+
+/** @brief Identity configuration of the device */
+#include "devicedata.h"
+
+/** @brief Define the number of objects that may be used in connections
+ *
+ *  This number needs only to consider additional objects. Connections to
+ *  the connection manager object as well as to the assembly object are supported
+ *  in any case.
+ */
+#define OPENER_CIP_NUM_APPLICATION_SPECIFIC_CONNECTABLE_OBJECTS 1
+
+/** @brief Define the number of supported explicit connections.
+ *  According to ODVA's PUB 70 this number should be greater than 6.
+ */
+#define OPENER_CIP_NUM_EXPLICIT_CONNS 6
+
+/** @brief Define the number of supported exclusive owner connections.
+ *  Each of these connections has to be configured with the function
+ *  void configureExclusiveOwnerConnectionPoint(unsigned int pa_unConnNum, unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, unsigned int pa_unConfigAssembly)
+ *
+ */
+#define OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS 1
+
+/** @brief Define the number of supported input only connections.
+ *  Each of these connections has to be configured with the function
+ *  void configureInputOnlyConnectionPoint(unsigned int pa_unConnNum, unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, unsigned int pa_unConfigAssembly)
+ *
+ */
+#define OPENER_CIP_NUM_INPUT_ONLY_CONNS 1
+
+/** @brief Define the number of supported input only connections per connection path
+ */
+#define OPENER_CIP_NUM_INPUT_ONLY_CONNS_PER_CON_PATH 3
+
+/** @brief Define the number of supported listen only connections.
+ *  Each of these connections has to be configured with the function
+ *  void configureListenOnlyConnectionPoint(unsigned int pa_unConnNum, unsigned int pa_unOutputAssembly, unsigned int pa_unInputAssembly, unsigned int pa_unConfigAssembly)
+ *
+ */
+#define OPENER_CIP_NUM_LISTEN_ONLY_CONNS 1
+
+/** @brief Define the number of supported Listen only connections per connection path
+ */
+#define OPENER_CIP_NUM_LISTEN_ONLY_CONNS_PER_CON_PATH   3
+
+/** @brief The number of bytes used for the buffer that will be used for generating any
+ *  reply data of messages. There are two uses in OpENer:
+ *    1. Explicit messages will use this buffer to store the data generated by the request
+ *    2. I/O Connections will use this buffer for the produced data
+ */
+#define OPENER_MESSAGE_DATA_REPLY_BUFFER 100
+
+/** @brief Number of sessions that can be handled at the same time
+ */
+#define OPENER_NUMBER_OF_SUPPORTED_SESSIONS 20
+
+/** @brief  The time in ms of the timer used in this implementations
+ */
+static const int kOpenerTimerTickInMilliSeconds = 10;
+
+/** @brief Define if RUN IDLE data is sent with consumed data
+ */
+static const int kOpenerConsumedDataHasRunIdleHeader = 1;
+
+/** @brief Define if RUN IDLE data is to be sent with produced data
+ *
+ * Per default we don't send run idle headers with produced data
+ */
+static const int kOpenerProducedDataHasRunIdleHeader = 0;
+
+#ifdef OPENER_WITH_TRACES
+/* If we have tracing enabled provide print tracing macro */
+#include <stdio.h>
+
+#define LOG_TRACE(...)  fprintf(stderr,__VA_ARGS__)
+
+/*#define PRINT_TRACE(args...)  fprintf(stderr,args);*/
+
+/** @brief A specialized assertion command that will log the assertion and block
+ *  further execution in an while(1) loop.
+ */
+#define OPENER_ASSERT(assertion) \
+  do { \
+    if( !(assertion) ) { \
+      LOG_TRACE("Assertion \"%s\" failed: file \"%s\", line %d\n", \
+                # assertion, \
+                __FILE__, \
+                __LINE__); \
+      while(1) {;} \
+    } \
+  } while(0);
+
+/* else use standard assert() */
+//#include <assert.h>
+//#include <stdio.h>
+//#define OPENER_ASSERT(assertion) assert(assertion)
+#else
+
+/* for release builds execute the assertion, but don't test it */
+//#define OPENER_ASSERT(assertion) (assertion)
+
+/* the above may result in "statement with no effect" warnings.
+ *  If you do not use assert()s to run functions, the an empty
+ *  macro can be used as below
+ */
+#define OPENER_ASSERT(assertion)
+/* else if you still want assertions to stop execution but without tracing, use the following */
+//#define OPENER_ASSERT(assertion) do { if(!(assertion)) { while(1){;} } } while (0)
+/* else use standard assert() */
+//#include <assert.h>
+//#include <stdio.h>
+//#define OPENER_ASSERT(assertion) assert(assertion)
+
+#endif
+
+/** @brief The number of bytes used for the Ethernet message buffer on
+ * the pc port. For different platforms it may makes sense to
+ * have more than one buffer.
+ *
+ *  This buffer size will be used for any received message.
+ *  The same buffer is used for the replied explicit message.
+ */
+#define PC_OPENER_ETHERNET_BUFFER_SIZE 512
+
+#endif /*OPENER_USER_CONF_H_*/

+ 149 - 0
source/src/ports/MINGW/sample_application/sampleapplication.c

@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2012, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include "opener_api.h"
+#include "appcontype.h"
+#include <string.h>
+#include <stdlib.h>
+
+#define DEMO_APP_INPUT_ASSEMBLY_NUM                100 //0x064
+#define DEMO_APP_OUTPUT_ASSEMBLY_NUM               150 //0x096
+#define DEMO_APP_CONFIG_ASSEMBLY_NUM               1 //0x001
+#define DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM  152 //0x098
+#define DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM 153 //0x099
+#define DEMO_APP_EXPLICT_ASSEMBLY_NUM              154 //0x09A
+
+/* global variables for demo application (4 assembly data fields)  ************/
+
+extern CipUint g_encapsulation_inactivity_timeout;
+
+EipUint8 g_assembly_data064[40]; /* Input */
+EipUint8 g_assembly_data096[40]; /* Output */
+EipUint8 g_assembly_data097[10]; /* Config */
+EipUint8 g_assembly_data09A[32]; /* Explicit */
+
+EipStatus ApplicationInitialization(void) {
+  /* create 3 assembly object instances*/
+  /*INPUT*/
+  CreateAssemblyObject( DEMO_APP_INPUT_ASSEMBLY_NUM, &g_assembly_data064[0],
+                        sizeof(g_assembly_data064) );
+
+  /*OUTPUT*/
+  CreateAssemblyObject( DEMO_APP_OUTPUT_ASSEMBLY_NUM, &g_assembly_data096[0],
+                        sizeof(g_assembly_data096) );
+
+  /*CONFIG*/
+  CreateAssemblyObject( DEMO_APP_CONFIG_ASSEMBLY_NUM, &g_assembly_data097[0],
+                        sizeof(g_assembly_data097) );
+
+  /*Heart-beat output assembly for Input only connections */
+  CreateAssemblyObject(DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM, 0, 0);
+
+  /*Heart-beat output assembly for Listen only connections */
+  CreateAssemblyObject(DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM, 0, 0);
+
+  /* assembly for explicit messaging */
+  CreateAssemblyObject( DEMO_APP_EXPLICT_ASSEMBLY_NUM, &g_assembly_data09A[0],
+                        sizeof(g_assembly_data09A) );
+
+  ConfigureExclusiveOwnerConnectionPoint(0, DEMO_APP_OUTPUT_ASSEMBLY_NUM,
+                                         DEMO_APP_INPUT_ASSEMBLY_NUM,
+                                         DEMO_APP_CONFIG_ASSEMBLY_NUM);
+  ConfigureInputOnlyConnectionPoint(0,
+                                    DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM,
+                                    DEMO_APP_INPUT_ASSEMBLY_NUM,
+                                    DEMO_APP_CONFIG_ASSEMBLY_NUM);
+  ConfigureListenOnlyConnectionPoint(0,
+                                     DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM,
+                                     DEMO_APP_INPUT_ASSEMBLY_NUM,
+                                     DEMO_APP_CONFIG_ASSEMBLY_NUM);
+
+  return kEipStatusOk;
+}
+
+void HandleApplication(void) {
+  /* check if application needs to trigger an connection */
+}
+
+void CheckIoConnectionEvent(unsigned int pa_unOutputAssembly,
+                            unsigned int pa_unInputAssembly,
+                            IoConnectionEvent pa_eIOConnectionEvent) {
+  /* maintain a correct output state according to the connection state*/
+
+  (void) pa_unOutputAssembly; /* suppress compiler warning */
+  (void) pa_unInputAssembly; /* suppress compiler warning */
+  (void) pa_eIOConnectionEvent; /* suppress compiler warning */
+}
+
+EipStatus AfterAssemblyDataReceived(CipInstance *pa_pstInstance) {
+  EipStatus nRetVal = kEipStatusOk;
+
+  /*handle the data received e.g., update outputs of the device */
+  switch (pa_pstInstance->instance_number) {
+    case DEMO_APP_OUTPUT_ASSEMBLY_NUM:
+      /* Data for the output assembly has been received.
+       * Mirror it to the inputs */
+      memcpy( &g_assembly_data064[0], &g_assembly_data096[0],
+              sizeof(g_assembly_data064) );
+      break;
+    case DEMO_APP_EXPLICT_ASSEMBLY_NUM:
+      /* do something interesting with the new data from
+       * the explicit set-data-attribute message */
+      break;
+    case DEMO_APP_CONFIG_ASSEMBLY_NUM:
+      /* Add here code to handle configuration data and check if it is ok
+       * The demo application does not handle config data.
+       * However in order to pass the test we accept any data given.
+       * EIP_ERROR
+       */
+      nRetVal = kEipStatusOk;
+      break;
+  }
+  return nRetVal;
+}
+
+EipBool8 BeforeAssemblyDataSend(CipInstance *pa_pstInstance) {
+  /*update data to be sent e.g., read inputs of the device */
+  /*In this sample app we mirror the data from out to inputs on data receive
+   * therefore we need nothing to do here. Just return true to inform that
+   * the data is new.
+   */
+
+  if (pa_pstInstance->instance_number == DEMO_APP_EXPLICT_ASSEMBLY_NUM) {
+    /* do something interesting with the existing data
+     * for the explicit get-data-attribute message */
+  }
+  return true;
+}
+
+EipStatus ResetDevice(void) {
+  /* add reset code here*/
+  CloseAllConnections();
+  return kEipStatusOk;
+}
+
+EipStatus ResetDeviceToInitialConfiguration(void) {
+  /*rest the parameters */
+  g_encapsulation_inactivity_timeout = 120;
+  /*than perform device reset*/
+  ResetDevice();
+  return kEipStatusOk;
+}
+
+void *
+CipCalloc(size_t pa_nNumberOfElements,
+          size_t pa_nSizeOfElement) {
+  return calloc(pa_nNumberOfElements, pa_nSizeOfElement);
+}
+
+void CipFree(void *pa_poData) {
+  free(pa_poData);
+}
+
+void RunIdleChanged(EipUint32 pa_nRunIdleValue) {
+  (void) pa_nRunIdleValue;
+}
+

+ 9 - 1
source/src/ports/POSIX/CMakeLists.txt

@@ -2,6 +2,14 @@ add_subdirectory(sample_application)
 
 set( PLATFORM_SPEC_SRC networkhandler.c opener_error.c networkconfig.c)
 
+#######################################
+# OpENer RT patch	              #
+#######################################
+set( OpENer_RT OFF CACHE BOOL "Activate OpENer RT" )
+if(OpENer_RT)
+  add_definitions( -DOPENER_RT )
+endif(OpENer_RT)
+
 #######################################
 # Add common includes                 #
 #######################################
@@ -16,4 +24,4 @@ add_library( ${OpENer_PLATFORM}PLATFORM ${PLATFORM_SPEC_SRC})
 
 add_executable(OpENer main.c)
 
-target_link_libraries( OpENer CIP Utils SAMPLE_APP ENET_ENCAP PLATFORM_GENERIC ${OpENer_PLATFORM}PLATFORM ${PLATFORM_SPEC_LIBS} ${OpENer_ADD_CIP_OBJECTS} rt)
+target_link_libraries( OpENer CIP Utils SAMPLE_APP ENET_ENCAP PLATFORM_GENERIC ${OpENer_PLATFORM}PLATFORM ${PLATFORM_SPEC_LIBS} ${OpENer_ADD_CIP_OBJECTS} rt cap pthread)

+ 116 - 12
source/src/ports/POSIX/main.c

@@ -6,6 +6,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <signal.h>
+#include <sys/capability.h>
+
+#ifdef OPENER_RT
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sched.h>
+#include <limits.h>
+#endif
 
 #include "generic_networkhandler.h"
 #include "opener_api.h"
@@ -22,6 +30,14 @@
  */
 void LeaveStack(int signal);
 
+/******************************************************************************/
+/** @brief Signal handler function for ending stack execution
+ *
+ * @param signal the signal we received
+ */
+void *executeEventLoop(
+  );
+
 /*****************************************************************************/
 /** @brief Flag indicating if the stack should end its execution
  */
@@ -30,7 +46,35 @@ int g_end_stack = 0;
 /******************************************************************************/
 int main(int argc,
          char *arg[]) {
-  EipUint16 unique_connection_id;
+
+  cap_t capabilities;
+  cap_value_t capabilies_list[1];
+
+  capabilities = cap_get_proc();
+  if (NULL == capabilities) {
+    printf("Could not get capabilities\n");
+    exit(0);
+  }
+
+  capabilies_list[0] = CAP_NET_RAW;
+  if (-1
+      == cap_set_flag(capabilities, CAP_EFFECTIVE, 1, capabilies_list,
+                      CAP_SET) ) {
+    cap_free(capabilities);
+    printf("Could not set CAP_NET_RAW capability\n");
+    exit(0);
+  }
+
+  if (-1 == cap_set_proc(capabilities) ) {
+    cap_free(capabilities);
+    printf("Could not push CAP_NET_RAW capability to process\n");
+    exit(0);
+  }
+
+  if (-1 == cap_free(capabilities) ) {
+    printf("Could not free capabilites value\n");
+    exit(0);
+  }
 
   if (argc != 2) {
     printf("Wrong number of command line parameters!\n");
@@ -53,33 +97,83 @@ int main(int argc,
     ConfigureMacAddress(arg[1]);
   }
 
-  /*for a real device the serial number should be unique per device */
+  /* for a real device the serial number should be unique per device */
   SetDeviceSerialNumber(123456789);
 
-  /* nUniqueConnectionID should be sufficiently random or incremented and stored
+  /* unique_connection_id should be sufficiently random or incremented and stored
    *  in non-volatile memory each time the device boots.
    */
-  unique_connection_id = rand();
+  EipUint16 unique_connection_id = rand();
 
   /* Setup the CIP Layer */
   CipStackInit(unique_connection_id);
 
   /* Setup Network Handles */
-  if ( kEipStatusOk == NetworkHandlerInitialize() ) {
+  if (kEipStatusOk == NetworkHandlerInitialize() ) {
     g_end_stack = 0;
 #ifndef WIN32
     /* register for closing signals so that we can trigger the stack to end */
     signal(SIGHUP, LeaveStack);
 #endif
+#ifdef OPENER_RT
+    /* Memory lock all*/
+    if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
+      OPENER_TRACE_ERR("mlockall failed: %m\n");
+      exit(-2);
+    }
+
+    struct sched_param param;
+    pthread_attr_t attr;
+    pthread_t thread;
+    CipUint ret = pthread_attr_init(&attr);
+    if (ret) {
+      OPENER_TRACE_ERR("init pthread attributes failed\n");
+      exit(-2);
+    }
+
+    /* Set stack size  */
+    ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
+    if (ret) {
+      OPENER_TRACE_ERR("setstacksize failed\n");
+      exit(-2);
+    }
+
+    /* Set policy and priority of the thread */
+    ret = pthread_attr_setschedpolicy(&attr, SCHED_RR);
+    if (ret) {
+      OPENER_TRACE_ERR("setschedpolicy failed\n");
+      exit(-2);
+    }
+    param.sched_priority = 80;
+    ret = pthread_attr_setschedparam(&attr, &param);
+    if (ret) {
+      OPENER_TRACE_ERR("pthread setschedparam failed\n");
+      exit(-2);
+    }
+    /* scheduling parameters */
+    ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+    if (ret) {
+      OPENER_TRACE_ERR("setinheritsched failed\n");
+      exit(-2);
+    }
 
-    /* The event loop. Put other processing you need done continually in here */
-    while (1 != g_end_stack) {
-      if ( kEipStatusOk != NetworkHandlerProcessOnce() ) {
-        OPENER_TRACE_ERR("Error in NetworkHandler loop! Exiting OpENer!\n");
-        break;
-      }
+    /* Create a thread with the specified attributes */
+    ret = pthread_create(&thread, &attr, executeEventLoop, NULL);
+    if (ret) {
+      OPENER_TRACE_ERR("create pthread failed\n");
+      exit(-2);
     }
 
+    /* Join the thread */
+    ret = pthread_join(thread, NULL);
+    if (ret) {
+      OPENER_TRACE_ERR("join pthread failed: %m\n");
+    }
+    /* Unlock memory */
+    munlockall();
+#else
+    executeEventLoop();
+#endif
     /* clean up network state */
     NetworkHandlerFinish();
   }
@@ -90,7 +184,17 @@ int main(int argc,
 }
 
 void LeaveStack(int signal) {
-  (void) signal; /* kill unused parameter warning */
+  (void) signal;       /* kill unused parameter warning */
   OPENER_TRACE_STATE("got signal HUP\n");
   g_end_stack = 1;
 }
+
+void *executeEventLoop() {
+  /* The event loop. Put other processing you need done continually in here */
+  while (1 != g_end_stack) {
+    if (kEipStatusOk != NetworkHandlerProcessOnce() ) {
+      OPENER_TRACE_ERR("Error in NetworkHandler loop! Exiting OpENer!\n");
+      break;
+    }
+  }
+}

+ 120 - 78
source/src/ports/POSIX/networkconfig.c

@@ -1,9 +1,11 @@
 /*******************************************************************************
- * Copyright (c) 2009, Rockwell Automation, Inc.
+ * Copyright (c) 2018, Rockwell Automation, Inc.
  * All rights reserved.
  *
  ******************************************************************************/
 #include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 #include "ciptcpipinterface.h"
 #include "networkconfig.h"
@@ -29,18 +31,19 @@
 void ConfigureMacAddress(const char *interface) {
   struct ifreq ifr;
   size_t if_name_len = strlen(interface);
-  if ( if_name_len < sizeof(ifr.ifr_name) ) {
+  if(if_name_len < sizeof(ifr.ifr_name) ) {
     memcpy(ifr.ifr_name, interface, if_name_len);
     ifr.ifr_name[if_name_len] = 0;
-  } else {
+  }
+  else{
     OPENER_TRACE_INFO("interface name is too long");
   }
 
   int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
-  if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
-    memcpy( &(g_ethernet_link.physical_address), &ifr.ifr_hwaddr.sa_data,
-            sizeof(g_ethernet_link.physical_address) );
+  if(ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
+    memcpy(&(g_ethernet_link.physical_address), &ifr.ifr_hwaddr.sa_data,
+           sizeof(g_ethernet_link.physical_address) );
   }
   CloseSocket(fd);
 }
@@ -49,23 +52,24 @@ EipStatus ConfigureNetworkInterface(const char *const network_interface) {
 
   struct ifreq ifr;
   size_t if_name_len = strlen(network_interface);
-  if ( if_name_len < sizeof(ifr.ifr_name) ) {
+  if(if_name_len < sizeof(ifr.ifr_name) ) {
     memcpy(ifr.ifr_name, network_interface, if_name_len);
     ifr.ifr_name[if_name_len] = 0;
-  } else {
+  }
+  else{
     OPENER_TRACE_INFO("interface name is too long\n");
   }
 
   int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
   int ipaddr = 0;
   int netaddr = 0;
-  if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
+  if(ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
     ipaddr = ( (struct sockaddr_in *) &ifr.ifr_addr )->sin_addr.s_addr;
   } else {
     return kEipStatusError;
   }
 
-  if (ioctl(fd, SIOCGIFNETMASK, &ifr) == 0) {
+  if(ioctl(fd, SIOCGIFNETMASK, &ifr) == 0) {
     netaddr = ( (struct sockaddr_in *) &ifr.ifr_netmask )->sin_addr.s_addr;
   } else {
     return kEipStatusError;
@@ -74,118 +78,156 @@ EipStatus ConfigureNetworkInterface(const char *const network_interface) {
   interface_configuration_.ip_address = ipaddr;
   interface_configuration_.network_mask = netaddr;
 
-  FILE *file_handle = fopen("/proc/net/route", "r");
-  char line[100] = { 0 };
-  char *string_part1 = NULL;
-  char *string_part2 = NULL;
+  char route_location[] = "/proc/net/route";
+
+  FILE *file_handle = fopen(route_location, "r");
+  char file_buffer[100] = { 0 };
   char *gateway_string = NULL;
   CipUdint gateway = 0;
 
-  while ( fgets(line, 100, file_handle) ) {
-    if ( strstr(line, network_interface) ) {
-      string_part1 = strtok(line, " \t");
-      string_part2 = strtok(NULL, " \t");
-      gateway_string = strtok(NULL, "\t");
+  if(file_handle) {
+    char *needle_start = NULL;
+    while(NULL ==
+          (needle_start =
+             strstr(file_buffer,
+                    network_interface) ) &&
+          fgets(file_buffer, sizeof(file_buffer), file_handle) ) {
+    }
+
+    if(NULL != needle_start) {
+      char *strtok_save = NULL;
+      strtok_r(needle_start, " \t", &strtok_save);
+      strtok_r(needle_start, " \t", &strtok_save);
+      gateway_string = strtok_r(needle_start, "\t", &strtok_save);
+    }
+    else{
+      OPENER_TRACE_ERR("network interface: %s not found", network_interface);
+      fclose(file_handle);
+      exit(1);
     }
   }
-  fclose(file_handle);
 
-  if (inet_pton(AF_INET, gateway_string, &gateway) == 1) {
-    if (gateway != LOOPBACK_BINARY) {
+  if(inet_pton(AF_INET, gateway_string, &gateway) == 1) {
+    if(LOOPBACK_BINARY != gateway) {
       interface_configuration_.gateway = gateway;
-    } else {
+    }
+    else{
       interface_configuration_.gateway = 0;
     }
-  } else {
+  }
+  else{
     interface_configuration_.gateway = 0;
   }
 
   /* calculate the CIP multicast address. The multicast address is calculated, not input*/
-  EipUint32 host_id = ntohl(interface_configuration_.ip_address)
-                      & ~ntohl(interface_configuration_.network_mask); /* see CIP spec 3-5.3 for multicast address algorithm*/
+  EipUint32 host_id = ntohl(interface_configuration_.ip_address) & ~ntohl(
+    interface_configuration_.network_mask);                                                                       /* see CIP spec 3-5.3 for multicast address algorithm*/
   host_id -= 1;
   host_id &= 0x3ff;
 
-  g_multicast_configuration.starting_multicast_address = htonl(
-    ntohl( inet_addr("239.192.1.0") ) + (host_id << 5) );
+  g_multicast_configuration.starting_multicast_address =
+    htonl(ntohl(inet_addr("239.192.1.0") ) + (host_id << 5) );
 
   CloseSocket(fd);
+  fclose(file_handle);
 
   return kEipStatusOk;
 }
 
 void ConfigureDomainName() {
-  FILE *file_handle = fopen("/etc/resolv.conf", "r");
-
-  char line[100] = { 0 };
-  char *string_part1 = NULL;
+  char resolv_conf_file[] = "/etc/resolv.conf";
+  FILE *file_handle = fopen(resolv_conf_file, "r");
+  char *file_buffer = NULL;
+  size_t file_length;
   char *domain_name_string = NULL;
   char *dns1_string = NULL;
   char *dns2_string = NULL;
-  CipBool done_domain = false;
-  CipBool done_n1 = false;
-
-  while ( fgets(line, 100, file_handle) ) {
-    if (strstr(line, "domain") && !done_domain) {
-      string_part1 = strtok(line, " ");
-      domain_name_string = strtok(NULL, "\n");
-
-      if (NULL != interface_configuration_.domain_name.string) {
-        /* if the string is already set to a value we have to free the resources
-         * before we can set the new value in order to avoid memory leaks.
-         */
-        CipFree(interface_configuration_.domain_name.string);
-      }
-      interface_configuration_.domain_name.length = strlen(domain_name_string);
-
-      if (interface_configuration_.domain_name.length) {
-        interface_configuration_.domain_name.string = (EipByte *) CipCalloc(
-          interface_configuration_.domain_name.length + 1, sizeof(EipInt8) );
-        strcpy(interface_configuration_.domain_name.string, domain_name_string);
-      } else {
-        interface_configuration_.domain_name.string = NULL;
-      }
-      done_domain = true;
-      continue;
-    }
 
-    if (strstr(line, "nameserver") && !done_n1) {
-      string_part1 = strtok(line, " ");
-      dns1_string = strtok(NULL, "\n");
+  if(file_handle) {
+    fseek(file_handle, 0, SEEK_END);
+    file_length = ftell(file_handle);
+    fseek(file_handle, 0, SEEK_SET);
+    file_buffer = malloc(file_length);
+    if(file_buffer) {
+      fread(file_buffer, 1, file_length, file_handle);
+      fclose(file_handle);
+    }
+    else{
+      OPENER_TRACE_ERR("Could not allocate memory for reading file %s\n",
+                       resolv_conf_file);
+      fclose(file_handle);
+      exit(1);
+    }
+  } else {
+    OPENER_TRACE_ERR("Could not open file %s\n", resolv_conf_file);
+    exit(1);
+  }
 
-      inet_pton(AF_INET, dns1_string, &interface_configuration_.name_server);
+  if(strstr(file_buffer, "domain") ) {
+    char *strtok_save = NULL;
+    strtok_r(file_buffer, " ", &strtok_save);
+    domain_name_string = strtok_r(file_buffer, "\n", &strtok_save);
 
-      done_n1 = true;
-      continue;
+    if(NULL != interface_configuration_.domain_name.string) {
+      /* if the string is already set to a value we have to free the resources
+       * before we can set the new value in order to avoid memory leaks.
+       */
+      CipFree(interface_configuration_.domain_name.string);
     }
-    if (strstr(line, "nameserver ") && done_n1) {
-      string_part1 = strtok(line, " ");
-      dns2_string = strtok(NULL, "\n");
-
-      inet_pton(AF_INET, dns2_string, &interface_configuration_.name_server_2);
-      break;
+    interface_configuration_.domain_name.length = strlen(domain_name_string);
+
+    if(interface_configuration_.domain_name.length) {
+      interface_configuration_.domain_name.string = (EipByte *) CipCalloc(
+        interface_configuration_.domain_name.length + 1,
+        sizeof(EipByte) );
+      snprintf(interface_configuration_.domain_name.string,
+               (interface_configuration_.domain_name.length + 1) *
+               sizeof(EipByte),
+               "%s",
+               domain_name_string);
+    }
+    else{
+      interface_configuration_.domain_name.string = NULL;
     }
   }
-  close(file_handle);
 
+  if(strstr(file_buffer, "nameserver") ) {
+    char *strtok_save = NULL;
+    strtok_r(file_buffer, " ", &strtok_save);
+    dns1_string = strtok_r(NULL, "\n", &strtok_save);
+
+    inet_pton(AF_INET, dns1_string, &interface_configuration_.name_server);
+  }
+
+  if(strstr(file_buffer, "nameserver ") ) {
+    char *strtok_save = NULL;
+    strtok_r(file_buffer, " ", &strtok_save);
+    dns2_string = strtok_r(file_buffer, "\n", &strtok_save);
+
+    inet_pton(AF_INET, dns2_string, &interface_configuration_.name_server_2);
+  }
+
+  free(file_buffer);
 }
 
 void ConfigureHostName() {
   char name[1024] = { 0 };
-  gethostname( name, sizeof(name) );
+  gethostname(name, sizeof(name) );
 
-  if (NULL != hostname_.string) {
+  if(NULL != hostname_.string) {
     /* if the string is already set to a value we have to free the resources
      * before we can set the new value in order to avoid memory leaks.
      */
     CipFree(hostname_.string);
   }
   hostname_.length = strlen(name);
-  if (hostname_.length) {
-    hostname_.string = (EipByte *) CipCalloc( hostname_.length + 1,
-                                              sizeof(EipByte) );
-    strcpy(hostname_.string, name);
-  } else {
+  if(hostname_.length) {
+    hostname_.string =
+      (EipByte *) CipCalloc(hostname_.length + 1, sizeof(EipByte) );
+    snprintf( (char *)hostname_.string,
+              (hostname_.length + 1) * sizeof(EipByte), "%s", name );
+  }
+  else{
     hostname_.string = NULL;
   }
 }

+ 2 - 4
source/src/ports/POSIX/networkhandler.c

@@ -19,7 +19,7 @@ MicroSeconds GetMicroSeconds(void) {
   struct timespec now = { .tv_nsec = 0, .tv_sec = 0 };
 
   int error = clock_gettime( CLOCK_MONOTONIC, &now );
-  OPENER_ASSERT(-1 != error);
+  OPENER_ASSERT(-1 != error)
   MicroSeconds micro_seconds =  (MicroSeconds)now.tv_nsec / 1000ULL +
                                now.tv_sec * 1000000ULL;
   return micro_seconds;
@@ -53,10 +53,8 @@ int SetSocketToNonBlocking(int socket_handle) {
                                              0) | O_NONBLOCK);
 }
 
-//TODO: Return setsocket return value
-int SetQosOnSocket(int socket,
+int SetQosOnSocket(const int socket,
                    CipUsint qos_value) {
-
   int set_tos = qos_value;
   return setsockopt(socket, IPPROTO_IP, IP_TOS, &set_tos, sizeof(set_tos) );
 }

+ 1 - 1
source/src/ports/POSIX/networkhandler.h

@@ -41,7 +41,7 @@ MicroSeconds GetMicroSeconds(void);
  */
 MilliSeconds GetMilliSeconds(void);
 
-int SetQosOnSocket(int socket,
+int SetQosOnSocket(const int socket,
                    CipUsint qos_value);
 
 #endif /* OPENER_NETWORKHANDLER_H_ */

+ 1 - 1
source/src/ports/POSIX/opener_error.c

@@ -4,7 +4,7 @@
  *
  ******************************************************************************/
 
-/** @file opener_error.c
+/** @file POSIX/opener_error.c
  *  @author Martin Melik Merkumians
  *  @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError
  *

+ 3 - 13
source/src/ports/POSIX/sample_application/opener_user_conf.h

@@ -6,7 +6,7 @@
 #ifndef OPENER_USER_CONF_H_
 #define OPENER_USER_CONF_H_
 
-/** @file opener_user_conf.h
+/** @file POSIX/sample_application/opener_user_conf.h
  * @brief OpENer configuration setup
  *
  * This file contains the general application specific configuration for OpENer.
@@ -89,16 +89,6 @@
  */
 static const MilliSeconds kOpenerTimerTickInMilliSeconds = 10;
 
-/** @brief Define if RUN IDLE data is sent with consumed data
- */
-static const int kOpenerConsumedDataHasRunIdleHeader = 1;
-
-/** @brief Define if RUN IDLE data is to be sent with produced data
- *
- * Per default we don't send run idle headers with produced data
- */
-static const int kOpenerProducedDataHasRunIdleHeader = 0;
-
 #ifdef OPENER_WITH_TRACES
 /* If we have tracing enabled provide print tracing macro */
 #include <stdio.h>
@@ -120,14 +110,14 @@ static const int kOpenerProducedDataHasRunIdleHeader = 0;
                 __LINE__); \
       while(1) {  } \
     } \
-  } while(0)
+  } while(0);
 
 /* else use standard assert() */
 //#include <assert.h>
 //#include <stdio.h>
 //#define OPENER_ASSERT(assertion) assert(assertion)
 #else
-#define OPENER_ASSERT(assertion) assert(assertion)
+#define OPENER_ASSERT(assertion) assert(assertion);
 #endif
 #else
 

+ 13 - 0
source/src/ports/POSIX/sample_application/sampleapplication.c

@@ -10,6 +10,8 @@
 
 #include "opener_api.h"
 #include "appcontype.h"
+#include "trace.h"
+#include "cipidentity.h"
 
 #define DEMO_APP_INPUT_ASSEMBLY_NUM                100 //0x064
 #define DEMO_APP_OUTPUT_ASSEMBLY_NUM               150 //0x096
@@ -103,6 +105,10 @@ EipStatus AfterAssemblyDataReceived(CipInstance *instance) {
        */
       status = kEipStatusOk;
       break;
+    default:
+      OPENER_TRACE_INFO(
+        "Unknown assembly instance ind AfterAssemblyDataReceived");
+      break;
   }
   return status;
 }
@@ -146,6 +152,13 @@ void CipFree(void *data) {
 }
 
 void RunIdleChanged(EipUint32 run_idle_value) {
+  OPENER_TRACE_INFO("Run/Idle handler triggered\n");
+  if( (0x0001 & run_idle_value) == 1 ) {
+    CipIdentitySetExtendedDeviceStatus(kAtLeastOneIoConnectionInRunMode);
+  } else {
+    CipIdentitySetExtendedDeviceStatus(
+      kAtLeastOneIoConnectionEstablishedAllInIdleMode);
+  }
   (void) run_idle_value;
 }
 

+ 2 - 3
source/src/ports/WIN32/networkhandler.c

@@ -45,8 +45,7 @@ int SetSocketToNonBlocking(int socket_handle) {
   return ioctlsocket(socket_handle, FIONBIO, &iMode);
 }
 
-int SetQosOnSocket(int socket,
+int SetQosOnSocket(const int socket,
                    CipUsint qos_value) {
-  CipUsint set_tos = qos_value;
-  return setsockopt(socket, IPPROTO_IP, IP_TOS, &set_tos, sizeof(set_tos) );
+  return 0; // Dummy implementation, until a working one is viable
 }

+ 1 - 1
source/src/ports/WIN32/opener_error.c

@@ -4,7 +4,7 @@
  *
  ******************************************************************************/
 
-/** @file opener_error.c
+/** @file WIN32/opener_error.c
  *  @author Martin Melik Merkumians
  *  @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError
  *

+ 9 - 14
source/src/ports/WIN32/sample_application/opener_user_conf.h

@@ -6,7 +6,7 @@
 #ifndef OPENER_USER_CONF_H_
 #define OPENER_USER_CONF_H_
 
-/** @file
+/** @file WIN32/sample_application/opener_user_conf.h
  * @brief OpENer configuration setup
  *
  * This file contains the general application specific configuration for OpENer.
@@ -27,6 +27,8 @@ typedef unsigned short in_port_t;
 /** @brief Identity configuration of the device */
 #include "devicedata.h"
 
+#include "typedefs.h"
+
 /** @brief Define the number of objects that may be used in connections
  *
  *  This number needs only to consider additional objects. Connections to
@@ -84,16 +86,6 @@ typedef unsigned short in_port_t;
  */
 static const int kOpenerTimerTickInMilliSeconds = 10;
 
-/** @brief Define if RUN IDLE data is sent with consumed data
- */
-static const int kOpenerConsumedDataHasRunIdleHeader = 1;
-
-/** @brief Define if RUN IDLE data is to be sent with produced data
- *
- * Per default we don't send run idle headers with produced data
- */
-static const int kOpenerProducedDataHasRunIdleHeader = 0;
-
 #ifdef OPENER_WITH_TRACES
 /* If we have tracing enabled provide print tracing macro */
 #include <stdio.h>
@@ -105,6 +97,7 @@ static const int kOpenerProducedDataHasRunIdleHeader = 0;
 /** @brief A specialized assertion command that will log the assertion and block
  *  further execution in an while(1) loop.
  */
+#ifdef IDLING_ASSERT
 #define OPENER_ASSERT(assertion) \
   do { \
     if( !(assertion) ) { \
@@ -114,8 +107,10 @@ static const int kOpenerProducedDataHasRunIdleHeader = 0;
                 __LINE__); \
       while(1) {;} \
     } \
-  } while(0)
-
+  } while(0);
+#else
+#define OPENER_ASSERT(assertion) assert(assertion);
+#endif
 /* else use standard assert() */
 //#include <assert.h>
 //#include <stdio.h>
@@ -123,7 +118,7 @@ static const int kOpenerProducedDataHasRunIdleHeader = 0;
 #else
 
 /* for release builds execute the assertion, but don't test it */
-#define OPENER_ASSERT(assertion) (assertion)
+#define OPENER_ASSERT(assertion) (assertion);
 
 /* the above may result in "statement with no effect" warnings.
  *  If you do not use assert()s to run functions, the an empty

+ 106 - 46
source/src/ports/generic_networkhandler.c

@@ -23,9 +23,12 @@
 #include "ciptcpipinterface.h"
 #include "opener_user_conf.h"
 #include "cipqos.h"
+#include "udp_protocol.h"
 
 #define MAX_NO_OF_TCP_SOCKETS 10
 
+extern CipTcpIpNetworkInterfaceConfiguration interface_configuration_;
+
 /** @brief handle any connection request coming in the TCP server socket.
  *
  */
@@ -106,7 +109,7 @@ EipStatus NetworkHandlerInitialize(void) {
   if ( ( g_network_status.udp_global_broadcast_listener = socket(AF_INET,
                                                                  SOCK_DGRAM,
                                                                  IPPROTO_UDP) )
-       == -1 ) {
+       == kEipInvalidSocket ) {
     int error_code = GetSocketErrorNumber();
     char *error_message = GetErrorMessage(error_code);
     OPENER_TRACE_ERR(
@@ -120,7 +123,7 @@ EipStatus NetworkHandlerInitialize(void) {
   /* create a new UDP socket */
   if ( ( g_network_status.udp_unicast_listener = socket(AF_INET, SOCK_DGRAM,
                                                         IPPROTO_UDP) ) ==
-       -1 ) {
+       kEipInvalidSocket ) {
     int error_code = GetSocketErrorNumber();
     char *error_message = GetErrorMessage(error_code);
     OPENER_TRACE_ERR("error allocating UDP unicast listener socket, %d - %s\n",
@@ -189,7 +192,7 @@ EipStatus NetworkHandlerInitialize(void) {
     return kEipStatusError;
   }
 
-  struct sockaddr_in global_broadcast_address = { .sin_family = AF_INET,
+  struct sockaddr_in global_broadcast_address = { .sin_family = {AF_INET},
                                                   .sin_port = htons(
                                                     kOpenerEthernetPort),
                                                   .sin_addr.s_addr = htonl(
@@ -302,7 +305,7 @@ void CheckAndHandleTcpListenerSocket(void) {
       return;
     }
 
-    if (SetQosOnSocket( new_socket, GetPriorityForSocket(0xFFF) ) <= 0) { /* got error */
+    if (SetQosOnSocket( new_socket, GetPriorityForSocket(0xFFFF) ) != 0) { /* got error */
       int error_code = GetSocketErrorNumber();
       char *error_message = GetErrorMessage(error_code);
       OPENER_TRACE_ERR(
@@ -327,7 +330,7 @@ void CheckAndHandleTcpListenerSocket(void) {
 //                        g_timestamps[i].last_update);
 //    }
 
-    OPENER_ASSERT(socket_timer != NULL);
+    OPENER_ASSERT(socket_timer != NULL)
 
     FD_SET(new_socket, &master_socket);
     /* add newfd to master set */
@@ -402,7 +405,7 @@ EipStatus NetworkHandlerProcessOnce(void) {
   g_last_time = g_actual_time;
   //OPENER_TRACE_INFO("Elapsed time: %u\n", g_network_status.elapsed_time);
 
-  /* check if we had been not able to update the connection manager for several OPENER_TIMER_TICK.
+  /* check if we had been not able to update the connection manager for several kOpenerTimerTickInMilliSeconds.
    * This should compensate the jitter of the windows timer
    */
   if (g_network_status.elapsed_time >= kOpenerTimerTickInMilliSeconds) {
@@ -432,9 +435,10 @@ void CheckAndHandleUdpGlobalBroadcastSocket(void) {
       "networkhandler: unsolicited UDP message on EIP global broadcast socket\n");
 
     /* Handle UDP broadcast messages */
+    CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = {0};
     int received_size = recvfrom(g_network_status.udp_global_broadcast_listener,
-                                 g_ethernet_communication_buffer,
-                                 PC_OPENER_ETHERNET_BUFFER_SIZE,
+                                 incoming_message,
+                                 sizeof(incoming_message),
                                  0, (struct sockaddr *) &from_address,
                                  &from_address_length);
 
@@ -451,12 +455,19 @@ void CheckAndHandleUdpGlobalBroadcastSocket(void) {
 
     OPENER_TRACE_INFO("Data received on global broadcast UDP:\n");
 
-    EipUint8 *receive_buffer = &g_ethernet_communication_buffer[0];
+    const EipUint8 *receive_buffer = &incoming_message[0];
     int remaining_bytes = 0;
     do {
+      ENIPMessage outgoing_message;
+      InitializeENIPMessage(&outgoing_message);
       int reply_length = HandleReceivedExplictUdpData(
-        g_network_status.udp_global_broadcast_listener, &from_address,
-        receive_buffer, received_size, &remaining_bytes, false);
+        g_network_status.udp_global_broadcast_listener,
+        &from_address,
+        receive_buffer,
+        received_size,
+        &remaining_bytes,
+        false,
+        &outgoing_message);
 
       receive_buffer += received_size - remaining_bytes;
       received_size = remaining_bytes;
@@ -466,7 +477,8 @@ void CheckAndHandleUdpGlobalBroadcastSocket(void) {
 
         /* if the active socket matches a registered UDP callback, handle a UDP packet */
         if (sendto( g_network_status.udp_global_broadcast_listener,
-                    (char *) g_ethernet_communication_buffer, reply_length, 0,
+                    (char *) outgoing_message.message_buffer,
+                    outgoing_message.used_message_length, 0,
                     (struct sockaddr *) &from_address, sizeof(from_address) )
             != reply_length) {
           OPENER_TRACE_INFO(
@@ -478,8 +490,6 @@ void CheckAndHandleUdpGlobalBroadcastSocket(void) {
 }
 
 void CheckAndHandleUdpUnicastSocket(void) {
-
-
   /* see if this is an unsolicited inbound UDP message */
   if ( true == CheckSocketSet(g_network_status.udp_unicast_listener) ) {
 
@@ -490,9 +500,10 @@ void CheckAndHandleUdpUnicastSocket(void) {
       "networkhandler: unsolicited UDP message on EIP unicast socket\n");
 
     /* Handle UDP broadcast messages */
+    CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = {0};
     int received_size = recvfrom(g_network_status.udp_unicast_listener,
-                                 g_ethernet_communication_buffer,
-                                 PC_OPENER_ETHERNET_BUFFER_SIZE,
+                                 incoming_message,
+                                 sizeof(incoming_message),
                                  0, (struct sockaddr *) &from_address,
                                  &from_address_length);
 
@@ -509,12 +520,14 @@ void CheckAndHandleUdpUnicastSocket(void) {
 
     OPENER_TRACE_INFO("Data received on UDP unicast:\n");
 
-    EipUint8 *receive_buffer = &g_ethernet_communication_buffer[0];
+    EipUint8 *receive_buffer = &incoming_message[0];
     int remaining_bytes = 0;
+    ENIPMessage outgoing_message;
+    InitializeENIPMessage(&outgoing_message);
     do {
       int reply_length = HandleReceivedExplictUdpData(
         g_network_status.udp_unicast_listener, &from_address, receive_buffer,
-        received_size, &remaining_bytes, true);
+        received_size, &remaining_bytes, true, &outgoing_message);
 
       receive_buffer += received_size - remaining_bytes;
       received_size = remaining_bytes;
@@ -524,7 +537,8 @@ void CheckAndHandleUdpUnicastSocket(void) {
 
         /* if the active socket matches a registered UDP callback, handle a UDP packet */
         if (sendto( g_network_status.udp_unicast_listener,
-                    (char *) g_ethernet_communication_buffer, reply_length, 0,
+                    (char *) outgoing_message.message_buffer,
+                    outgoing_message.used_message_length, 0,
                     (struct sockaddr *) &from_address, sizeof(from_address) )
             != reply_length) {
           OPENER_TRACE_INFO(
@@ -536,12 +550,37 @@ void CheckAndHandleUdpUnicastSocket(void) {
 }
 
 EipStatus SendUdpData(struct sockaddr_in *address,
-                      int socket,
+                      int socket_handle,
                       EipUint8 *data,
                       EipUint16 data_length) {
 
-  int sent_length = sendto( socket, (char *) data, data_length, 0,
-                            (struct sockaddr *) address, sizeof(*address) );
+
+
+  OPENER_TRACE_INFO("UDP port to be sent to: %x\n", ntohs(address->sin_port) );
+  UDPHeader header = {
+    .source_port = 2222,
+    .destination_port = ntohs(address->sin_port),
+    .packet_length = kUpdHeaderLength + data_length,
+    .checksum = 0
+  };
+
+  char complete_message[PC_OPENER_ETHERNET_BUFFER_SIZE];
+  memcpy(complete_message + kUpdHeaderLength, data, data_length);
+  UDPHeaderGenerate(&header, (char *)complete_message);
+  UDPHeaderSetChecksum(&header,
+                       htons(UDPHeaderCalculateChecksum(complete_message,
+                                                        8 + data_length,
+                                                        interface_configuration_
+                                                        .ip_address,
+                                                        address->sin_addr.s_addr) ) );
+  UDPHeaderGenerate(&header, (char *)complete_message);
+
+  int sent_length = sendto( socket_handle,
+                            (char *) complete_message,
+                            data_length + kUpdHeaderLength,
+                            0,
+                            (struct sockaddr *) address,
+                            sizeof(*address) );
 
   if (sent_length < 0) {
     int error_code = GetSocketErrorNumber();
@@ -554,7 +593,7 @@ EipStatus SendUdpData(struct sockaddr_in *address,
     return kEipStatusError;
   }
 
-  if (sent_length != data_length) {
+  if (sent_length != data_length + kUpdHeaderLength) {
     OPENER_TRACE_WARN(
       "data length sent_length mismatch; probably not all data was sent in SendUdpData, sent %d of %d\n",
       sent_length,
@@ -577,7 +616,9 @@ EipStatus HandleDataOnTcpSocket(int socket) {
      fit*/
 
   /*Check how many data is here -- read the first four bytes from the connection */
-  long number_of_read_bytes = recv(socket, g_ethernet_communication_buffer, 4,
+  CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = {0};
+
+  long number_of_read_bytes = recv(socket, incoming_message, 4,
                                    0); /*TODO we may have to set the socket to a non blocking socket */
 
   SocketTimer *socket_timer = SocketTimerArrayGetSocketTimer(
@@ -610,7 +651,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
     return kEipStatusError;
   }
 
-  const EipUint8 *read_buffer = &g_ethernet_communication_buffer[2]; /* at this place EIP stores the data length */
+  const EipUint8 *read_buffer = &incoming_message[2]; /* at this place EIP stores the data length */
   size_t data_size = GetIntFromMessage(&read_buffer)
                      + ENCAPSULATION_HEADER_LENGTH - 4; /* -4 is for the 4 bytes we have already read*/
   /* (NOTE this advances the buffer pointer) */
@@ -623,7 +664,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
       OPENER_TRACE_INFO(
         "Entering consumption loop, remaining data to receive: %zu\n",
         data_sent);
-      number_of_read_bytes = recv(socket, &g_ethernet_communication_buffer[0],
+      number_of_read_bytes = recv(socket, &incoming_message[0],
                                   data_sent, 0);
 
       if (number_of_read_bytes == 0) /* got error or connection closed by client */
@@ -660,7 +701,7 @@ EipStatus HandleDataOnTcpSocket(int socket) {
     return kEipStatusOk;
   }
 
-  number_of_read_bytes = recv(socket, &g_ethernet_communication_buffer[4],
+  number_of_read_bytes = recv(socket, &incoming_message[4],
                               data_size, 0);
 
   if (0 == number_of_read_bytes) /* got error or connection closed by client */
@@ -710,9 +751,11 @@ EipStatus HandleDataOnTcpSocket(int socket) {
       FreeErrorMessage(error_message);
     }
 
-    number_of_read_bytes = HandleReceivedExplictTcpData(
-      socket, g_ethernet_communication_buffer, data_size, &remaining_bytes,
-      &sender_address);
+    ENIPMessage outgoing_message = {0};
+    InitializeENIPMessage(&outgoing_message);
+    int number_of_bytes_to_send = HandleReceivedExplictTcpData(
+      socket, incoming_message, data_size, &remaining_bytes,
+      &sender_address, &outgoing_message);
     SocketTimer *socket_timer = SocketTimerArrayGetSocketTimer(
       g_timestamps,
       OPENER_NUMBER_OF_SUPPORTED_SESSIONS,
@@ -729,17 +772,17 @@ EipStatus HandleDataOnTcpSocket(int socket) {
         remaining_bytes);
     }
 
-    if (number_of_read_bytes > 0) {
+    if (number_of_bytes_to_send > 0) {
       OPENER_TRACE_INFO("TCP reply sent:\n");
 
-      data_sent = send(socket, (char *) &g_ethernet_communication_buffer[0],
-                       number_of_read_bytes, 0);
+      data_sent = send(socket, (char *) outgoing_message.message_buffer,
+                       outgoing_message.used_message_length, 0);
       SocketTimer *socket_timer = SocketTimerArrayGetSocketTimer(
         g_timestamps,
         OPENER_NUMBER_OF_SUPPORTED_SESSIONS,
         socket);
       SocketTimerSetLastUpdate(socket_timer, g_actual_time);
-      if (data_sent != number_of_read_bytes) {
+      if (data_sent != number_of_bytes_to_send) {
         OPENER_TRACE_WARN("TCP response was not fully sent\n");
       }
     }
@@ -758,10 +801,10 @@ EipStatus HandleDataOnTcpSocket(int socket) {
 
 /** @brief create a new UDP socket for the connection manager
  *
- * @param communciation_direction Consuming or producing port
+ * @param communication_direction Consuming or producing port
  * @param socket_data Data for socket creation
  *
- * @return the socket handle if successful, else -1 */
+ * @return the socket handle if successful, else kEipInvalidSocket */
 int CreateUdpSocket(UdpCommuncationDirection communication_direction,
                     struct sockaddr_in *socket_data,
                     CipUsint qos_for_socket) {
@@ -770,8 +813,15 @@ int CreateUdpSocket(UdpCommuncationDirection communication_direction,
 
   socklen_t peer_address_length = sizeof(struct sockaddr_in);
   /* create a new UDP socket */
-  if ( ( new_socket =
-           socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) ) == kEipInvalidSocket ) {
+  if(kUdpCommuncationDirectionConsuming == communication_direction) {
+    new_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  }
+
+  if(kUdpCommuncationDirectionProducing == communication_direction) {
+    new_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
+  }
+
+  if (new_socket == kEipInvalidSocket) {
     int error_code = GetSocketErrorNumber();
     char *error_message = GetErrorMessage(error_code);
     OPENER_TRACE_ERR("networkhandler: cannot create UDP socket: %d- %s\n",
@@ -785,11 +835,11 @@ int CreateUdpSocket(UdpCommuncationDirection communication_direction,
     OPENER_TRACE_ERR(
       "error setting socket to non-blocking on new socket\n");
     CloseSocket(new_socket);
-    OPENER_ASSERT(false); /* This should never happen! */
+    OPENER_ASSERT(false) /* This should never happen! */
     return kEipStatusError;
   }
 
-  if (SetQosOnSocket(new_socket, GetPriorityForSocket(qos_for_socket) ) <= 0) { /* got error */
+  if (SetQosOnSocket(new_socket, GetPriorityForSocket(qos_for_socket) ) != 0) { /* got error */
     int error_code = GetSocketErrorNumber();
     char *error_message = GetErrorMessage(error_code);
     OPENER_TRACE_ERR(
@@ -828,6 +878,11 @@ int CreateUdpSocket(UdpCommuncationDirection communication_direction,
     OPENER_TRACE_INFO("networkhandler: bind UDP socket %d\n", new_socket);
   } else { /* we have a producing udp socket */
 
+    int option_value = 1;
+    setsockopt( new_socket, SOL_SOCKET, SO_REUSEADDR,
+                (char *) &option_value,
+                sizeof(option_value) );
+
     if (socket_data->sin_addr.s_addr
         == g_multicast_configuration.starting_multicast_address) {
       if (1 != g_time_to_live_value) { /* we need to set a TTL value for the socket */
@@ -864,8 +919,11 @@ int CreateUdpSocket(UdpCommuncationDirection communication_direction,
     socket_data->sin_addr.s_addr = peer_address.sin_addr.s_addr;
   }
 
-  /* add new socket to the master list                                             */
-  FD_SET(new_socket, &master_socket);
+  if (kUdpCommuncationDirectionConsuming == communication_direction) {
+    /* add new socket to the master list */
+    FD_SET(new_socket, &master_socket);
+  }
+
   if (new_socket > highest_socket_handle) {
     OPENER_TRACE_INFO("New highest socket: %d\n", new_socket);
     highest_socket_handle = new_socket;
@@ -883,18 +941,21 @@ void CheckAndHandleConsumingUdpSockets(void) {
     current_connection_object = (CipConnectionObject *)iterator->data;
     iterator = iterator->next; /* do this at the beginning as the close function may can make the entry invalid */
 
-    if ( (-1
+    if ( (kEipInvalidSocket
           != current_connection_object->socket[
             kUdpCommuncationDirectionConsuming])
          && ( true
               == CheckSocketSet(
                 current_connection_object->socket[
                   kUdpCommuncationDirectionConsuming]) ) ) {
+      OPENER_TRACE_INFO("Processing UDP consuming message\n");
       struct sockaddr_in from_address = {0};
       socklen_t from_address_length = sizeof(from_address);
+      CipOctet incoming_message[PC_OPENER_ETHERNET_BUFFER_SIZE] = {0};
+
       int received_size = recvfrom(
         current_connection_object->socket[kUdpCommuncationDirectionConsuming],
-        g_ethernet_communication_buffer, PC_OPENER_ETHERNET_BUFFER_SIZE, 0,
+        incoming_message, sizeof(incoming_message), 0,
         (struct sockaddr *) &from_address, &from_address_length);
       if (0 == received_size) {
         int error_code = GetSocketErrorNumber();
@@ -911,7 +972,6 @@ void CheckAndHandleConsumingUdpSockets(void) {
         continue;
       }
 
-
       if (0 > received_size) {
         int error_code = GetSocketErrorNumber();
         char *error_message = GetErrorMessage(error_code);
@@ -927,7 +987,7 @@ void CheckAndHandleConsumingUdpSockets(void) {
         continue;
       }
 
-      HandleReceivedConnectedData(g_ethernet_communication_buffer,
+      HandleReceivedConnectedData(incoming_message,
                                   received_size, &from_address);
 
     }

+ 1 - 1
source/src/ports/generic_networkhandler.h

@@ -29,7 +29,7 @@
 
 SocketTimer g_timestamps[OPENER_NUMBER_OF_SUPPORTED_SESSIONS];
 
-EipUint8 g_ethernet_communication_buffer[PC_OPENER_ETHERNET_BUFFER_SIZE]; /**< communication buffer */
+//EipUint8 g_ethernet_communication_buffer[PC_OPENER_ETHERNET_BUFFER_SIZE]; /**< communication buffer */
 
 fd_set master_socket;
 fd_set read_socket;

+ 1 - 1
source/src/ports/socket_timer.h

@@ -22,7 +22,7 @@ typedef struct socket_timer {
  * Sets socket of a Socket Timer
  *
  * @param socket_timer Socket Timer to be set
- * @param Socket value
+ * @param socket Socket handle
  */
 void SocketTimerSetSocket(SocketTimer *const socket_timer,
                           const int socket);

+ 100 - 0
source/src/ports/udp_protocol.c

@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2018, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include "udp_protocol.h"
+
+#ifdef WIN32
+#include <winsock.h>
+#endif
+
+void UDPHeaderSetSourcePort(UDPHeader *const header,
+                            const uint16_t source_port) {
+  header->source_port = source_port;
+}
+
+uint16_t UDPHeaderGetSourcePort(const UDPHeader *const header) {
+  return header->source_port;
+}
+
+void UDPHeaderSetDestinationPort(UDPHeader *const header,
+                                 const uint16_t destination_port) {
+  header->destination_port = destination_port;
+}
+
+uint16_t UDPHeaderGetDestinationPort(const UDPHeader *const header) {
+  return header->destination_port;
+}
+
+void UDPHeaderSetPacketLength(UDPHeader *const header,
+                              const uint16_t packet_length) {
+  header->packet_length = packet_length;
+}
+
+uint16_t UDPHeaderGetPacketLength(const UDPHeader *const header) {
+  return header->packet_length;
+}
+
+void UDPHeaderSetChecksum(UDPHeader *const header,
+                          const uint16_t checksum) {
+  header->checksum = checksum;
+}
+
+uint16_t UDPHeaderGetChecksum(const UDPHeader *const header) {
+  return header->checksum;
+}
+
+void UDPHeaderGenerate(const UDPHeader *header,
+                       char *message) {
+  *( (uint16_t *)message ) = htons(UDPHeaderGetSourcePort(header) );
+  message += 2;
+  *( (uint16_t *)message ) = htons(UDPHeaderGetDestinationPort(header) );
+  message += 2;
+  *( (uint16_t *)message ) = htons(UDPHeaderGetPacketLength(header) );
+  message += 2;
+  *( (uint16_t *)message ) = htons(UDPHeaderGetChecksum(header) );
+  message += 2;
+}
+
+uint16_t UDPHeaderCalculateChecksum(const void *udp_packet,
+                                    const size_t udp_packet_length,
+                                    const in_addr_t source_addr,
+                                    const in_addr_t destination_addr) {
+  const uint16_t *udp_packet_words = udp_packet;
+  uint32_t checksum = 0;
+  size_t length = udp_packet_length;
+
+  // Process UDP packet
+  while(length > 1) {
+    checksum += *udp_packet_words++;
+    if(checksum & 0x8000000) {
+      checksum = (checksum & 0xFFFF) + (checksum >> 16);
+    }
+    length -= 2;
+  }
+
+  if(0 != length % 2) {
+    // Add padding if packet length is odd
+    checksum += *( (uint8_t *)udp_packet_words );
+  }
+
+  //Process IP pseudo header
+  uint16_t *source_addr_as_words = (void *)&source_addr;
+  checksum += *source_addr_as_words + *(source_addr_as_words + 1);
+
+  uint16_t *destination_addr_as_words = (void *)&destination_addr;
+  checksum += *destination_addr_as_words + *(destination_addr_as_words + 1);
+
+  checksum += htons(IPPROTO_UDP);
+  checksum += htons(udp_packet_length);
+
+  //Add the carries
+  while(0 != checksum >> 16) {
+    checksum = (checksum & 0xFFFF) + (checksum >> 16);
+  }
+
+  // Return one's complement
+  return (uint16_t)(~checksum);
+}

+ 124 - 0
source/src/ports/udp_protocol.h

@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2018, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+/** @file udp_protocol.h
+ *  @author Martin Melik Merkumians
+ *  @brief Includes a basic set of operations for UDP header creation and checksum calculation
+ *
+ *  In order to send UDP packets from a specified source port, the UDP header creation has to
+ *  be done by hand. This file specifies the interface for OpENer's UDP header management,
+ *  creation, and checksum calculation
+ */
+
+#ifndef SRC_PORTS_UDP_PROTOCOL_H_
+#define SRC_PORTS_UDP_PROTOCOL_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#ifdef OPENER_POSIX
+#include <netinet/in.h>
+#elif WIN32
+typedef uint32_t in_addr_t;
+#endif
+
+static size_t kUpdHeaderLength = 8U; /**< UDP header length in bytes */
+
+/** @brief Representing the needed information for the UDP header
+ *
+ * This struct represents the UDP header information
+ */
+typedef struct {
+  uint16_t source_port;   /**< UDP source port */
+  uint16_t destination_port;   /**< UDP destination port */
+  uint16_t packet_length;   /**< UDP packet length (data + header) */
+  uint16_t checksum;   /**< UDP checksum */
+} UDPHeader;
+
+/** @brief Sets source port field
+ *
+ * @param header The UDP header struct instance
+ * @param source_port Source port value to be set
+ */
+void UDPHeaderSetSourcePort(UDPHeader *const header,
+                            const uint16_t source_port);
+
+/** @brief Gets source port field
+ *
+ * @param header The header struct instance
+ * @return The source port
+ */
+uint16_t UDPHeaderGetSourcePort(const UDPHeader *const header);
+
+/** @brief Sets destination port field
+ *
+ * @param header The UDP header struct instance
+ * @param destination_port Destination port value to be set
+ */
+void UDPHeaderSetDestinationPort(UDPHeader *const header,
+                                 const uint16_t destination_port);
+
+/** @brief Gets destination port field
+ *
+ * @param header The header struct instance
+ * @return The destination port
+ */
+uint16_t UDPHeaderGetDestinationPort(const UDPHeader *const header);
+
+/** @brief Sets packet length field
+ *
+ * @param header The UDP header struct instance
+ * @param packet_length Length value to be set
+ */
+void UDPHeaderSetPacketLength(UDPHeader *const header,
+                              const uint16_t packet_length);
+
+/** @brief Gets packet length field
+ *
+ * @param header The header struct instance
+ * @return The packet length
+ */
+uint16_t UDPHeaderGetPacketLength(const UDPHeader *const header);
+
+/** @brief Sets checksum field
+ *
+ * @param header The UDP header struct instance
+ * @param checksum Checksum value to be set
+ */
+void UDPHeaderSetChecksum(UDPHeader *const header,
+                          const uint16_t checksum);
+
+/** @brief Gets checksum field
+ *
+ * @param header The UDP header struct instance
+ * @return The packet length
+ */
+uint16_t UDPHeaderGetChecksum(const UDPHeader *const header);
+
+/** @brief Calculates the checksum based on the set UDP packet data and pseudo IP header
+ *
+ * @param udp_packet the UDP packet including the UDP header
+ * @param udp_packet_length UPD packet length
+ * @param source_addr The IP source address
+ * @param destination_addr The IP destination address
+ * @return The calculated checksum
+ */
+uint16_t UDPHeaderCalculateChecksum(const void *udp_packet,
+                                    const size_t udp_packet_length,
+                                    const in_addr_t source_addr,
+                                    const in_addr_t destination_addr);
+
+/** @brief Generate the UDP header in the message according to the header
+ *
+ * The function generates the UDP header according to the header struct
+ * overwriting the first 8 bytes
+ *
+ * @param header The UDP header struct instance
+ * @param message The message buffer
+ */
+void UDPHeaderGenerate(const UDPHeader *header,
+                       char *message);
+
+#endif /* SRC_PORTS_UDP_PROTOCOL_H_ */

+ 1 - 1
source/src/utils/CMakeLists.txt

@@ -2,6 +2,6 @@
 opener_common_includes()
 opener_platform_spec()
 
-set( UTILS_SRC random.c xorshiftrandom.c doublylinkedlist.c)
+set( UTILS_SRC random.c xorshiftrandom.c doublylinkedlist.c  enipmessage.c)
 
 add_library( Utils ${UTILS_SRC} )

+ 11 - 9
source/src/utils/doublylinkedlist.c

@@ -7,6 +7,8 @@
 #include "doublylinkedlist.h"
 
 #include "opener_user_conf.h"
+#include <stdio.h>  // Needed to define NULL
+#include <assert.h>
 
 void DoublyLinkedListInitialize(DoublyLinkedList *list,
                                 NodeMemoryAllocator allocator,
@@ -42,15 +44,15 @@ DoublyLinkedListNode *DoublyLinkedListNodeCreate(
 
 void DoublyLinkedListNodeDestroy(const DoublyLinkedList *const list,
                                  DoublyLinkedListNode **node) {
-  OPENER_ASSERT(list->deallocator != NULL);
+  OPENER_ASSERT(list->deallocator != NULL)
   list->deallocator(node);
 }
 
 void DoublyLinkedListInsertAtHead(DoublyLinkedList *const list,
                                   void *data) {
-  OPENER_ASSERT(list->allocator != NULL);
-  DoublyLinkedListNode *new_node = DoublyLinkedListNodeCreate(data,
-                                                              list->allocator);
+  OPENER_ASSERT(list->allocator != NULL)
+  DoublyLinkedListNode * new_node = DoublyLinkedListNodeCreate(data,
+                                                               list->allocator);
   if(NULL == list->first) {
     list->first = new_node;
     list->last = new_node;
@@ -63,9 +65,9 @@ void DoublyLinkedListInsertAtHead(DoublyLinkedList *const list,
 
 void DoublyLinkedListInsertAtTail(DoublyLinkedList *const list,
                                   const void *const data) {
-  OPENER_ASSERT(list->allocator != NULL);
-  DoublyLinkedListNode *new_node = DoublyLinkedListNodeCreate(data,
-                                                              list->allocator);
+  OPENER_ASSERT(list->allocator != NULL)
+  DoublyLinkedListNode * new_node = DoublyLinkedListNodeCreate(data,
+                                                               list->allocator);
   if(NULL == list->last) {
     list->first = new_node;
     list->last = new_node;
@@ -79,7 +81,7 @@ void DoublyLinkedListInsertAtTail(DoublyLinkedList *const list,
 void DoublyLinkedListInsertBeforeNode(DoublyLinkedList *const list,
                                       DoublyLinkedListNode *node,
                                       void *data) {
-  OPENER_ASSERT(list->allocator != NULL);
+  OPENER_ASSERT(list->allocator != NULL)
   if(list->first == node) {
     DoublyLinkedListInsertAtHead(list, data);
   } else {
@@ -95,7 +97,7 @@ void DoublyLinkedListInsertBeforeNode(DoublyLinkedList *const list,
 void DoublyLinkedListInsertAfterNode(DoublyLinkedList *const list,
                                      DoublyLinkedListNode *node,
                                      void *data) {
-  OPENER_ASSERT(list->allocator != NULL);
+  OPENER_ASSERT(list->allocator != NULL)
   if(list->last == node) {
     DoublyLinkedListInsertAtTail(list, data);
   } else {

+ 1 - 1
source/src/utils/doublylinkedlist.h

@@ -8,7 +8,7 @@
 #define SRC_UTILS_DOUBLYLINKEDLIST_H_
 
 /**
- * @file doublelinkedlist.h
+ * @file doublylinkedlist.h
  *
  * The public interface for a reference type doubly linked list
  */

+ 13 - 0
source/src/utils/enipmessage.c

@@ -0,0 +1,13 @@
+/*******************************************************************************
+ * Copyright (c) 2018, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include "enipmessage.h"
+#include "string.h"
+
+void InitializeENIPMessage(ENIPMessage *const message) {
+  memset(message, 0, sizeof(ENIPMessage) );
+  message->current_message_position = message->message_buffer;
+}

+ 19 - 0
source/src/utils/enipmessage.h

@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2018, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#ifndef SRC_CIP_ENIPMESSAGE_H_
+#define SRC_CIP_ENIPMESSAGE_H_
+
+#include "opener_user_conf.h"
+
+typedef struct enip_message {
+  CipOctet message_buffer[PC_OPENER_ETHERNET_BUFFER_SIZE];
+  CipOctet *current_message_position;
+  size_t used_message_length;
+} ENIPMessage;
+
+void InitializeENIPMessage(ENIPMessage *const message);
+
+#endif /* SRC_CIP_ENIPMESSAGE_H_ */

+ 3 - 1
source/tests/CMakeLists.txt

@@ -7,6 +7,7 @@
 #######################################
 add_test_includes()
 
+opener_common_includes()
 
 ###################################################
 # Copy custom test output file to binary location #
@@ -17,12 +18,13 @@ add_subdirectory( cip )
 add_subdirectory( ports )
 add_subdirectory( enet_encap )
 add_subdirectory( utils )
+
 add_executable( OpENer_Tests OpENerTests.cpp )
 
 find_library ( CPPUTEST_LIBRARY CppUTest ${CPPUTEST_HOME}/cpputest_build/lib )
 find_library ( CPPUTESTEXT_LIBRARY CppUTestExt ${CPPUTEST_HOME}/cpputest_build/lib )
 
-target_link_libraries( OpENer_Tests CIP ENET_ENCAP PLATFORM_GENERIC SAMPLE_APP ${OpENer_PLATFORM}PLATFORM rt )
+target_link_libraries( OpENer_Tests rt )
 
 target_link_libraries( OpENer_Tests gcov ${CPPUTEST_LIBRARY} ${CPPUTESTEXT_LIBRARY} )
 target_link_libraries( OpENer_Tests UtilsTest Utils ) 

+ 6 - 0
source/tests/OpENerTests.cpp

@@ -1,10 +1,16 @@
 #include "OpENerTests.h"
 
+extern "C" {
+#include "endianconv.h"
+}
+
 int main(int argc,
          char **argv) {
   /* These checks are here to make sure assertions outside test runs don't crash */
   CHECK(true);
   LONGS_EQUAL(1, 1);
 
+  DetermineEndianess();
+
   return CommandLineTestRunner::RunAllTests(argc, argv);
 }

+ 2 - 0
source/tests/OpENerTests.h

@@ -3,9 +3,11 @@
 IMPORT_TEST_GROUP(RandomClass);
 IMPORT_TEST_GROUP(XorShiftRandom);
 IMPORT_TEST_GROUP(EndianConversion);
+IMPORT_TEST_GROUP(CipCommon);
 IMPORT_TEST_GROUP(CipEpath);
 IMPORT_TEST_GROUP(CipElectronicKey);
 IMPORT_TEST_GROUP(CipConnectionManager);
 IMPORT_TEST_GROUP(CipConnectionObject);
 IMPORT_TEST_GROUP(SocketTimer);
 IMPORT_TEST_GROUP(DoublyLinkedList);
+IMPORT_TEST_GROUP(EncapsulationProtocol);

+ 1 - 1
source/tests/cip/CMakeLists.txt

@@ -8,7 +8,7 @@ opener_common_includes()
 #######################################
 opener_platform_support("INCLUDES")
 
-set( CipTestSrc cipepathtest.cpp cipelectronickeytest.cpp cipconnectionmanagertest.cpp cipconnectionobjecttest.cpp )
+set( CipTestSrc cipepathtest.cpp cipelectronickeytest.cpp cipconnectionmanagertest.cpp cipconnectionobjecttest.cpp cipcommontests.cpp)
 
 include_directories( ${SRC_DIR}/cip )
 

+ 266 - 0
source/tests/cip/cipcommontests.cpp

@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2018, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include <CppUTest/TestHarness.h>
+#include <stdint.h>
+#include <string.h>
+
+extern "C" {
+
+#include "cipcommon.h"
+
+}
+
+TEST_GROUP(CipCommon) {
+
+};
+
+TEST(CipCommon, GetSizeOfAttributeCipBool) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipBool;
+  CHECK_EQUAL(sizeof(CipBool), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipSint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipSint;
+  CHECK_EQUAL(sizeof(CipSint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipInt) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipInt;
+  CHECK_EQUAL(sizeof(CipInt), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipDint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipDint;
+  CHECK_EQUAL(sizeof(CipDint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipUsint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipUsint;
+  CHECK_EQUAL(sizeof(CipUsint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipUint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipUint;
+  CHECK_EQUAL(sizeof(CipUint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipUdint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipUdint;
+  CHECK_EQUAL(sizeof(CipUdint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipReal) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipReal;
+  CHECK_EQUAL(sizeof(CipReal), GetSizeOfAttribute(&attribute) );
+}
+
+#ifdef OPENER_SUPPORT_64BIT_DATATYPES
+TEST(CipCommon, GetSizeOfAttributeCipLreal) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipLreal;
+  CHECK_EQUAL(sizeof(CipLreal), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipUlint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipUlint;
+  CHECK_EQUAL(sizeof(CipUlint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipLint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipLint;
+  CHECK_EQUAL(sizeof(CipLint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipLword) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipLword;
+  CHECK_EQUAL(sizeof(CipLword), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipLtime) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipLtime;
+  CHECK_EQUAL(sizeof(CipLint), GetSizeOfAttribute(&attribute) );
+}
+#endif /* OPENER_SUPPORT_64BIT_DATATYPES */
+
+TEST(CipCommon, GetSizeOfAttributeCipStime) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipStime;
+  CHECK_EQUAL(sizeof(CipDint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipData) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipDate;
+  CHECK_EQUAL(sizeof(CipUint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipTimeOfDay) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipTimeOfDay;
+  CHECK_EQUAL(sizeof(CipUdint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipDateAndTime) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipDateAndTime;
+  CHECK_EQUAL(sizeof(CipUdint) + sizeof(CipUint),
+              GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipString) {
+  CipAttributeStruct attribute;
+  char demo_string[] = "Hello World!";
+  CipString test_string =
+  { .length = sizeof(demo_string), .string = (EipByte *)demo_string };
+  attribute.type = kCipString;
+  attribute.data = (void *)&test_string;
+
+  CHECK_EQUAL(
+    sizeof(test_string.length) + test_string.length * sizeof(CipOctet),
+    GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipByte) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipByte;
+  CHECK_EQUAL(sizeof(CipByte), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipWord) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipWord;
+  CHECK_EQUAL(sizeof(CipWord), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipDword) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipDword;
+  CHECK_EQUAL(sizeof(CipDword), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipString2) {
+  CipAttributeStruct attribute;
+  char demo_string[] = "H e l l o   W o r l d !"; /* \0 termination symbol is seen as second byte for ! */
+  CipString test_string =
+  { .length = sizeof(demo_string) / 2, .string = (EipByte *)demo_string };
+  attribute.type = kCipString;
+  attribute.data = (void *)&test_string;
+
+  CHECK_EQUAL(
+    sizeof(test_string.length) + test_string.length * sizeof(CipOctet),
+    GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipFtime) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipFtime;
+  CHECK_EQUAL(sizeof(CipDint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipItime) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipItime;
+  CHECK_EQUAL(sizeof(CipInt), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipStringN) {
+  CipAttributeStruct attribute;
+  char demo_string[] = "Hello World!";
+  CipStringN test_string =
+  { .size = 1, .length = sizeof(demo_string),
+    .string = (EipByte *) demo_string };
+  attribute.type = kCipStringN;
+  attribute.data = (void *) &test_string;
+
+  CHECK_EQUAL(
+    sizeof(test_string.size) + sizeof(test_string.length) + test_string.size * test_string.length *
+    sizeof(CipOctet),
+    GetSizeOfAttribute(
+      &attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipTime) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipTime;
+  CHECK_EQUAL(sizeof(CipDint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipEpath) {
+  /* TODO: Fix me */
+  CipAttributeStruct attribute;
+  attribute.type = kCipItime;
+  CHECK_EQUAL(sizeof(CipInt), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipEngUnit) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipEngUnit;
+  CHECK_EQUAL(sizeof(CipUint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipUsintUsint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipUsintUsint;
+  CHECK_EQUAL(2 * sizeof(CipUsint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipUdintUdintUdintUdintUdintString) {
+  CipTcpIpNetworkInterfaceConfiguration config;
+  char domain_name[] = "www.github.com/EIPStackGroup/OpENer";
+  config.domain_name.length = sizeof(domain_name);
+  config.domain_name.string = (EipByte *)domain_name;
+  CipAttributeStruct attribute;
+  attribute.type = kCipUdintUdintUdintUdintUdintString;
+  attribute.data = (void *)&config;
+  CHECK_EQUAL(
+    5 * sizeof(CipUdint) + sizeof(CipUint) + sizeof(domain_name) *
+    sizeof(EipByte),
+    GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCip6Usint) {
+  CipAttributeStruct attribute;
+  attribute.type = kCip6Usint;
+  CHECK_EQUAL(6 * sizeof(CipUsint), GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipMemberList) {
+  CipAttributeStruct attribute;
+  attribute.type = kCipMemberList;
+  CHECK_EQUAL(0, GetSizeOfAttribute(&attribute) );
+  /* Currently not implemented */
+}
+
+TEST(CipCommon, GetSizeOfAttributeCipByteArray) {
+  CipByte data_array[] = {1,2,3,4,5,6,7,8,9};
+  CipByteArray array;
+  array.data = (EipByte *)&data_array;
+  array.length = sizeof(data_array);
+  CipAttributeStruct attribute;
+  attribute.type = kCipByteArray;
+  attribute.data = (void *)&array;
+  CHECK_EQUAL(sizeof(CipUint) + array.length * sizeof(CipByte),
+              GetSizeOfAttribute(&attribute) );
+}
+
+TEST(CipCommon, GetSizeOfAttributeInternalUint6) {
+  CipAttributeStruct attribute;
+  attribute.type = kInternalUint6;
+  CHECK_EQUAL(6 * sizeof(CipUint), GetSizeOfAttribute(&attribute) );
+}

+ 58 - 8
source/tests/cip/cipconnectionobjecttest.cpp

@@ -18,62 +18,112 @@ TEST_GROUP(CipConnectionObject) {
 
 };
 
-TEST(CipConnectionObject, StateNonExistent) {
+/* Get State tests */
+TEST(CipConnectionObject, GetStateNonExistent) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 0;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateNonExistent, state);
 }
 
-TEST(CipConnectionObject, StateConfiguring) {
+TEST(CipConnectionObject, GetStateConfiguring) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 1;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateConfiguring, state);
 }
 
-TEST(CipConnectionObject, StateWaitingForConnectionID) {
+TEST(CipConnectionObject, GetStateWaitingForConnectionID) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 2;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateWaitingForConnectionID, state);
 }
 
-TEST(CipConnectionObject, StateEstablished) {
+TEST(CipConnectionObject, GetStateEstablished) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 3;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateEstablished, state);
 }
 
-TEST(CipConnectionObject, StateTimedOut) {
+TEST(CipConnectionObject, GetStateTimedOut) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 4;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateTimedOut, state);
 }
 
-TEST(CipConnectionObject, StateDeferredDelete) {
+TEST(CipConnectionObject, GetStateDeferredDelete) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 5;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateDeferredDelete, state);
 }
 
-TEST(CipConnectionObject, StateClosing) {
+TEST(CipConnectionObject, GetStateClosing) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 6;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateClosing, state);
 }
 
-TEST(CipConnectionObject, StateInvalid) {
+TEST(CipConnectionObject, GetStateInvalid) {
   CipConnectionObject connection_object = { 0 };
   connection_object.state = 7;
   ConnectionObjectState state = ConnectionObjectGetState(&connection_object);
   CHECK_EQUAL(kConnectionObjectStateInvalid, state);
 }
 
+/* Set state tests */
+TEST(CipConnectionObject, SetStateNonExistent) {
+  CipConnectionObject connection_object = { 0 };
+  ConnectionObjectSetState(&connection_object,
+                           kConnectionObjectStateNonExistent);
+  CHECK_EQUAL(0, connection_object.state);
+}
+
+TEST(CipConnectionObject, SetStateConfiguring) {
+  CipConnectionObject connection_object = { 0 };
+  ConnectionObjectSetState(&connection_object,
+                           kConnectionObjectStateConfiguring);
+  CHECK_EQUAL(1, connection_object.state);
+}
+
+TEST(CipConnectionObject, SetStateWaitingForConnectionID) {
+  CipConnectionObject connection_object = { 0 };
+  ConnectionObjectSetState(&connection_object,
+                           kConnectionObjectStateWaitingForConnectionID);
+  CHECK_EQUAL(2, connection_object.state);
+}
+
+TEST(CipConnectionObject, SetStateEstablished) {
+  CipConnectionObject connection_object = { 0 };
+  ConnectionObjectSetState(&connection_object,
+                           kConnectionObjectStateEstablished);
+  CHECK_EQUAL(3, connection_object.state);
+}
+
+TEST(CipConnectionObject, SetStateTimedOut) {
+  CipConnectionObject connection_object = { 0 };
+  ConnectionObjectSetState(&connection_object, kConnectionObjectStateTimedOut);
+  CHECK_EQUAL(4, connection_object.state);
+}
+
+TEST(CipConnectionObject, SetStateDeferredDelete) {
+  CipConnectionObject connection_object = { 0 };
+  ConnectionObjectSetState(&connection_object,
+                           kConnectionObjectStateDeferredDelete);
+  CHECK_EQUAL(5, connection_object.state);
+}
+
+TEST(CipConnectionObject, SetStateClosing) {
+  CipConnectionObject connection_object = { 0 };
+  ConnectionObjectSetState(&connection_object, kConnectionObjectStateClosing);
+  CHECK_EQUAL(6, connection_object.state);
+}
+
+/* Get InstanceType tests */
 TEST(CipConnectionObject, InstanceTypeIExplicitMessaging) {
   CipConnectionObject connection_object = { 0 };
   connection_object.instance_type =

+ 48 - 0
source/tests/cip/cipepathtest.cpp

@@ -66,6 +66,54 @@ TEST(CipEpath, GetSegmentTypeReserved) {
   SegmentType segment_type = GetPathSegmentType(message);
   CHECK_EQUAL(kSegmentTypeReserved, segment_type);
 }
+/* Set Segment Type tests*/
+TEST(CipEpath, SetSegmentTypePortSegment) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypePortSegment, message);
+  CHECK_EQUAL(0x00, message[0]);
+}
+
+TEST(CipEpath, SetSegmentTypeLogicalSegment) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypeLogicalSegment, message);
+  CHECK_EQUAL(0x20, message[0]);
+}
+
+TEST(CipEpath, SetSegmentTypeNetworkSegment) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypeNetworkSegment, message);
+  CHECK_EQUAL(0x40, message[0]);
+}
+
+TEST(CipEpath, SetSegmentTypeSymbolicSegment) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypeSymbolicSegment, message);
+  CHECK_EQUAL(0x60, message[0]);
+}
+
+TEST(CipEpath, SetSegmentTypeDataSegment) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypeDataSegment, message);
+  CHECK_EQUAL(0x80, message[0]);
+}
+
+TEST(CipEpath, SetSegmentTypeDataTypeConstructed) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypeDataTypeConstructed, message);
+  CHECK_EQUAL(0xA0, message[0]);
+}
+
+TEST(CipEpath, SetSegmentTypeDataTypeElementary) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypeDataTypeElementary, message);
+  CHECK_EQUAL(0xC0, message[0]);
+}
+
+TEST(CipEpath, SetSegmentTypeReserved) {
+  unsigned char message[] = {0xFF};
+  SetPathSegmentType(kSegmentTypeReserved, message);
+  CHECK_EQUAL(0xE0, message[0]);
+}
 
 /** Port segment tests **/
 TEST(CipEpath, GetPortSegmentExtendedAddressSizeTrue) {

+ 1 - 1
source/tests/enet_encap/CMakeLists.txt

@@ -8,7 +8,7 @@ opener_common_includes()
 #######################################
 opener_platform_support("INCLUDES")
 
-set( EthernetEncapsulationTestSrc endianconvtest.cpp )
+set( EthernetEncapsulationTestSrc endianconvtest.cpp encaptest.cpp)
 
 include_directories( ${SRC_DIR}/enet_encap )
 

+ 130 - 0
source/tests/enet_encap/encaptest.cpp

@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2018, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+
+#include <CppUTest/TestHarness.h>
+#include <stdint.h>
+#include <string.h>
+
+extern "C" {
+
+#include "encap.h"
+
+#include "ciptypes.h"
+#include "enipmessage.h"
+
+}
+
+TEST_GROUP(EncapsulationProtocol) {
+
+};
+
+TEST(EncapsulationProtocol, AnswerListIdentityRequest) {
+
+  CipOctet incoming_message[] =
+    "\x63\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd7\xdd\x00\x00" \
+    "\x00\x00\x00\x00\x00\x00\x00\x00";
+
+  CipOctet expected_outgoing_message[] =
+    "\x63\x00\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd7\xdd\x00\x00" \
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x0c\x00\x2b\x00\x01\x00" \
+    "\x00\x02\xaf\x12\xc0\xa8\x38\x65\x00\x00\x00\x00\x00\x00\x00\x00" \
+    "\x01\x00\x0c\x00\xe9\xfd\x02\x01\x00\x00\x15\xcd\x5b\x07\x09\x4f" \
+    "\x70\x45\x4e\x65\x72\x20\x50\x43\xff";
+
+  ENIPMessage outgoing_message;
+  InitializeENIPMessage(&outgoing_message);
+
+  EncapsulationData receive_data;
+  CreateEncapsulationStructure(incoming_message,
+                               sizeof(incoming_message),
+                               &receive_data);
+
+  EncapsulateListIdentityResponseMessage(&receive_data, &outgoing_message);
+
+}
+
+TEST(EncapsulationProtocol, AnswerListServicesRequest) {
+  CipOctet incoming_message[] =
+    "\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xdd\x00\x00" \
+    "\x00\x00\x00\x00\x00\x00\x00\x00";
+
+  CipOctet expected_outgoing_message[] =
+    "\x04\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xdd\x00\x00" \
+    "\x00\x00\x00\x00\x00\x00\x00\x00";
+
+  ENIPMessage outgoing_message;
+  InitializeENIPMessage(&outgoing_message);
+
+  EncapsulationData recieved_data = {0};
+  CreateEncapsulationStructure(incoming_message,
+                               sizeof(incoming_message),
+                               &recieved_data);
+
+  HandleReceivedListServicesCommand(&recieved_data, &outgoing_message);
+
+}
+
+TEST(EncapsulationProtocol, AnswerListInterfacesRequest) {
+  CipOctet incoming_message[] = "";
+
+  CipOctet expected_outgoing_message[] = "";
+
+  ENIPMessage outgoing_message;
+  InitializeENIPMessage(&outgoing_message);
+
+  EncapsulationData received_data = {0};
+  CreateEncapsulationStructure(incoming_message,
+                               sizeof(incoming_message),
+                               &received_data);
+
+  HandleReceivedListInterfacesCommand(&received_data, &outgoing_message);
+}
+
+TEST(EncapsulationProtocol, AnswerRegisterSessionRequestWrongProtocolVersion) {
+
+  CipOctet incoming_message[] =
+    "\x65\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x67\x88\x00\x00" \
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+
+  CipOctet expected_outgoing_message[] = "";
+
+  ENIPMessage outgoing_message;
+  InitializeENIPMessage(&outgoing_message);
+
+  EncapsulationData received_data = {0};
+  CreateEncapsulationStructure(incoming_message,
+                               sizeof(incoming_message),
+                               &received_data);
+
+  HandleReceivedRegisterSessionCommand(0, &received_data, &outgoing_message);
+
+}
+
+TEST(EncapsulationProtocol, SendRRData) {
+  CipOctet incoming_message[] =
+    "\x6f\x00\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\xf0\xdd\x00\x00" \
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" \
+    "\x01\x00\x00\x00";
+
+  CipOctet expected_outgoing_message[] = "";
+
+  ENIPMessage outgoing_message = {0};
+  InitializeENIPMessage(&outgoing_message);
+
+  EncapsulationData received_data = {0};
+  CreateEncapsulationStructure(incoming_message,
+                               sizeof(incoming_message),
+                               &received_data);
+
+  struct sockaddr_in fake_originator = {0};
+  struct sockaddr *fake_originator_pointer =
+    (struct sockaddr *)&fake_originator;
+
+  HandleReceivedSendRequestResponseDataCommand(&received_data,
+                                               fake_originator_pointer,
+                                               &outgoing_message);
+}

+ 16 - 0
travis_scripts/compileGcovResults.sh

@@ -0,0 +1,16 @@
+#!/usr/bin/env sh
+
+# Delete old gcov folder
+rm -rf gcov_results
+
+# Create new gcov folder
+mkdir gcov_results
+# Change into results folder
+cd gcov_results
+# Get all object files
+OBJECTS=$(find ../src -iname "*.o")
+# Process all files
+for o in $OBJECTS; 
+do 
+  gcov -abcfu $o; 
+done

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików