Pārlūkot izejas kodu

Improved Cmake scripts for multiple platform support

CapXilinx 12 gadi atpakaļ
vecāks
revīzija
410dd8c13b

+ 4 - 0
.gitignore

@@ -1 +1,5 @@
 bin/
+doc/
+.project
+.cproject
+

+ 56 - 16
CMakeLists.txt

@@ -1,12 +1,12 @@
 #######################################
 # Required CMake version              #
 #######################################
-cmake_minimum_required(VERSION 2.8.9)
+cmake_minimum_required( VERSION 2.8.9 )
 
 #######################################
 # Project name                        #
 #######################################
-project(OpENer)
+project( OpENer C )
 
 #######################################
 # Project version                     #
@@ -14,33 +14,73 @@ project(OpENer)
 set( OpENer_VERSION_MAJOR 1 )
 set( OpENer_VERSION_MINOR 2 )
 
+set ( OpENer_BUILDSUPPORT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/buildsupport"
+ CACHE PATH "OpENer build support directory.")
+ 
+INCLUDE( ${OpENer_BUILDSUPPORT_DIR}/OpENer.cmake)
+
+#######################################
+# Platform switches                   #
+#######################################
+set( OpENer_KNOWN_PLATFORMS "POSIX" "WIN32" )
+
+set( OpENer_PLATFORM CACHE STRINGS "Platform OpENer will be built for" )
+set_property(CACHE OpENer_PLATFORM PROPERTY STRINGS ${OpENer_KNOWN_PLATFORMS} )
+
 #######################################
-# Project-wide switches               #
+# OpENer tracer switches              #
 #######################################
-set( PLATFORM_POSIX OFF CACHE BOOL "Platform for OpENer is POSIX" )
-set( PLATFORM_WIN32 OFF CACHE BOOL "Platform for OpENer is WIN32" )
+set( OpENer_TRACES OFF CACHE BOOL "Activate OpENer traces" )
+if(OpENer_TRACES)
+  add_definitions( -DOPENER_WITH_TRACES )
+  set( TRACE_LEVEL 0 )
+  set( OpENer_TRACE_LEVEL_ERROR ON CACHE BOOL "Error trace level" )
+  set( OpENer_TRACE_LEVEL_WARNING ON CACHE BOOL "Warning trace level" )
+  set( OpENer_TRACE_LEVEL_STATE ON CACHE BOOL "State trace level" )
+  set( OpENer_TRACE_LEVEL_INFO ON CACHE BOOL "Info trace level" )
+  
+  if(OpENer_TRACE_LEVEL_ERROR)
+    math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 1" )
+  endif(OpENer_TRACE_LEVEL_ERROR)
+  if(OpENer_TRACE_LEVEL_WARNING)
+    math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 2" )
+  endif(OpENer_TRACE_LEVEL_WARNING)
+  if(OpENer_TRACE_LEVEL_STATE)
+    math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 4" )
+  endif(OpENer_TRACE_LEVEL_STATE)
+  if(OpENer_TRACE_LEVEL_INFO)
+    math( EXPR TRACE_LEVEL "${TRACE_LEVEL} + 8" )
+  endif(OpENer_TRACE_LEVEL_INFO)
+  
+  add_definitions(-DOPENER_TRACE_LEVEL=${TRACE_LEVEL})
+  
+endif(OpENer_TRACES)
 
-set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DOPENER_WITH_TRACES -DOPENER_TRACE_LEVEL=0xFF -w -Wall" )
+set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w -Wall" )
 
 #######################################
 # Add subdirectories                  #
 #######################################
 add_subdirectory(src)
 
+#######################################
+# Set source directories              #
+#######################################
 set( SRC_DIR "${PROJECT_SOURCE_DIR}/src" )
 set( CIP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/cip" )
 set( ENET_ENCAP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/enet_encap" )
 set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
 
+#######################################
+# Include directories                 #
+#######################################
+
 include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
 
-if(PLATFORM_POSIX)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application)
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -std=c99" )
-elseif(PLATFORM_WIN32)
-  find_path( MSINTTYPES_INCLUDES inttypes.h ${PROJECT_SOURCE_DIR}/contrib/msinttypes) 
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application ${MSINTTYPES_INCLUDES})
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32 -Za" )
-else(PLATFORM_POSIX)
-  message(FATAL_ERROR "No platform selected!")
-endif(PLATFORM_POSIX)
+#######################################
+# Add platfrom specific things        #
+#######################################
+
+opener_platform_support("INCLUDES")
+
+

+ 19 - 0
buildsupport/OpENer.cmake

@@ -0,0 +1,19 @@
+FUNCTION(opener_add_definition)
+  FOREACH(ARG ${ARGV})
+    set_property(GLOBAL APPEND PROPERTY OPENER_DEFINITION ${ARG})
+  ENDFOREACH(ARG)
+ENDFUNCTION(opener_add_definition)
+
+macro(opener_platform_support ARGS)
+
+  if(OpENer_PLATFORM STREQUAL "") #Hier Fehler korrigieren!
+    message(FATAL_ERROR "No platform selected!")  
+  endif(OpENer_PLATFORM STREQUAL "")
+
+  include( ${OpENer_BUILDSUPPORT_DIR}/${OpENer_PLATFORM}/OpENer_PLATFORM_${ARGS}.cmake)
+  opener_platform_spec()
+endmacro(opener_platform_support ARGS)
+ 
+macro(opener_general_includes)
+  
+endmacro(opener_general_includes)

+ 5 - 0
buildsupport/POSIX/OpENer_PLATFORM_INCLUDES.cmake

@@ -0,0 +1,5 @@
+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} -ansi -std=c99" )
+endmacro(opener_platform_spec)
+

+ 5 - 0
buildsupport/WIN32/OpENer_PLATFORM_INCLUDES.cmake

@@ -0,0 +1,5 @@
+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} -DWIN32 -Za" )
+endmacro(opener_platform_includes)
+

+ 1 - 8
src/cip/CMakeLists.txt

@@ -9,14 +9,7 @@ set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
 
 include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
 
-if(PLATFORM_POSIX)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application)
-elseif(PLATFORM_WIN32)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application ${MSINTTYPES_INCLUDES})
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32" )
-else(PLATFORM_POSIX)
-  message(FATAL_ERROR "No platform selected!")
-endif(PLATFORM_POSIX)
+opener_platform_support("INCLUDES")
 
 set( CIP_SRC appcontype.c cipassembly.c cipclass3connection.c cipcommon.c cipconnectionmanager.c ciperror.h cipethernetlink.c cipidentity.c cipioconnection.c cipmessagerouter.c ciptcpipinterface.c ciptypes.h )
 

+ 1 - 8
src/enet_encap/CMakeLists.txt

@@ -11,13 +11,6 @@ set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
 
 include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
 
-if(PLATFORM_POSIX)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application)
-elseif(PLATFORM_WIN32)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application ${MSINTTYPES_INCLUDES})
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32" )
-else(PLATFORM_POSIX)
-  message(FATAL_ERROR "No platform selected!")
-endif(PLATFORM_POSIX)
+opener_platform_support("INCLUDES")
 
 add_library( ENET_ENCAP ${ENET_ENCAP_SRC} )

+ 2 - 10
src/ports/CMakeLists.txt

@@ -1,13 +1,5 @@
 #######################################
-# OpENer ports			      #
+# OpENer ports			              #
 #######################################
 
-if(PLATFORM_POSIX)
-  add_subdirectory(platform-pc)
-elseif(PLATFORM_WIN32)
-  add_subdirectory(platform-pc)
-else(PLATFORM_POSIX)
-  message(FATAL_ERROR "No platform selected!")
-endif(PLATFORM_POSIX)
-
-
+add_subdirectory( ${OpENer_PLATFORM} )

+ 1 - 9
src/ports/platform-pc/CMakeLists.txt → src/ports/POSIX/CMakeLists.txt

@@ -9,15 +9,7 @@ set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
 
 include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
 
-if(PLATFORM_POSIX)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application)
-elseif(PLATFORM_WIN32)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application ${MSINTTYPES_INCLUDES})
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32" )
-  set( PLATFORM_SPEC_LIBS ws2_32)
-else(PLATFORM_POSIX)
-  message(FATAL_ERROR "No platform selected!")
-endif(PLATFORM_POSIX)
+opener_platform_support("INCLUDES")
 
 add_library( PCPLATFORM ${PLATFORM_SPEC_SRC}) 
 

+ 0 - 0
src/ports/platform-pc/main.c → src/ports/POSIX/main.c


+ 0 - 0
src/ports/platform-pc/networkhandler.c → src/ports/POSIX/networkhandler.c


+ 0 - 0
src/ports/platform-pc/networkhandler.h → src/ports/POSIX/networkhandler.h


+ 10 - 0
src/ports/POSIX/sample_application/CMakeLists.txt

@@ -0,0 +1,10 @@
+set( SRC_DIR "${PROJECT_SOURCE_DIR}/src" )
+set( CIP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/cip" )
+set( ENET_ENCAP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/enet_encap" )
+set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
+
+include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
+
+opener_platform_support("INCLUDES")
+
+add_library(SAMPLE_APP sampleapplication.c)

+ 0 - 0
src/ports/platform-pc/sample_application/opener_user_conf.h → src/ports/POSIX/sample_application/opener_user_conf.h


+ 0 - 0
src/ports/platform-pc/sample_application/sampleapplication.c → src/ports/POSIX/sample_application/sampleapplication.c


+ 18 - 0
src/ports/WIN32/CMakeLists.txt

@@ -0,0 +1,18 @@
+add_subdirectory(sample_application)
+
+set( PLATFORM_SPEC_SRC networkhandler.c)
+
+set( SRC_DIR "${PROJECT_SOURCE_DIR}/src" )
+set( CIP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/cip" )
+set( ENET_ENCAP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/enet_encap" )
+set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
+
+include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
+
+opener_platform_support("INCLUDES")
+
+add_library( PCPLATFORM ${PLATFORM_SPEC_SRC}) 
+
+add_executable(OpENer main.c)
+
+target_link_libraries( OpENer PCPLATFORM CIP SAMPLE_APP ENET_ENCAP ${PLATFORM_SPEC_LIBS} )

+ 109 - 0
src/ports/WIN32/main.c

@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved. 
+ *
+ ******************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "networkhandler.h"
+#include "opener_api.h"
+#include "cipcommon.h"
+#include "trace.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_nEndStack = 0;
+
+/******************************************************************************/
+int
+main(int argc, char *arg[])
+{
+  EIP_UINT8 acMyMACAddress[6];
+  EIP_UINT16 nUniqueConnectionID;
+
+  if (argc != 12)
+    {
+      printf("Wrong number of command line parameters!\n");
+      printf("The correct command line parameters are:\n");
+      printf(
+          "./opener ipaddress subnetmask gateway domainname hostaddress macaddress\n");
+      printf(
+          "    e.g. ./opener 192.168.0.2 255.255.255.0 192.168.0.1 test.com testdevice 00 15 C5 BF D0 87\n");
+      exit(0);
+    }
+  else
+    {
+      /* fetch Internet address info from the platform */
+      configureNetworkInterface(arg[1], arg[2], arg[3]);
+      configureDomainName(arg[4]);
+      configureHostName(arg[5]);
+
+      acMyMACAddress[0] = (EIP_UINT8) strtoul(arg[6], NULL, 16);
+      acMyMACAddress[1] = (EIP_UINT8) strtoul(arg[7], NULL, 16);
+      acMyMACAddress[2] = (EIP_UINT8) strtoul(arg[8], NULL, 16);
+      acMyMACAddress[3] = (EIP_UINT8) strtoul(arg[9], NULL, 16);
+      acMyMACAddress[4] = (EIP_UINT8) strtoul(arg[10], NULL, 16);
+      acMyMACAddress[5] = (EIP_UINT8) strtoul(arg[11], NULL, 16);
+      configureMACAddress(acMyMACAddress);
+    }
+
+  /*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 */
+  CIP_Init(nUniqueConnectionID);
+
+  /* Setup Network Handles */
+  if (EIP_OK == NetworkHandler_Init())
+    {
+      g_nEndStack = 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_nEndStack)
+        {
+          if( EIP_OK != NetworkHandler_ProcessOnce())
+            {
+              break;
+            }
+        }
+
+      /* clean up network state */
+      NetworkHandler_Finish();
+    }
+  /* close remaining sessions and connections, cleanup used data */
+  shutdownCIP();
+
+  return -1;
+}
+
+void
+leaveStack(int pa_nSig)
+{
+  (void) pa_nSig; /* kill unused parameter warning */
+  OPENER_TRACE_STATE("got signal HUP\n");
+  g_nEndStack = 1;
+}

+ 686 - 0
src/ports/WIN32/networkhandler.c

@@ -0,0 +1,686 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#include <Ws2tcpip.h>
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+#include <opener_api.h>
+#include "networkhandler.h"
+#include <encap.h>
+#include <cipconnectionmanager.h>
+#include <endianconv.h>
+#include <trace.h>
+#include <ciptcpipinterface.h>
+
+/* values needed from the connection manager */
+extern S_CIP_ConnectionObject *g_pstActiveConnectionList;
+/* communication buffer */EIP_UINT8 g_acPCEthernetCommBuffer[PC_OPENER_ETHERNET_BUFFER_SIZE];
+
+#define MAX_NO_OF_TCP_SOCKETS 10
+
+typedef long MILLISECONDS;
+fd_set master;
+fd_set read_fds;
+/* temporary file descriptor for select() */
+
+int fdmax;
+
+/*!< This var holds the TCP socket the received to last explicit message.
+ * It is needed for opening point to point connection to determine the peer's
+ * address.
+ */
+int g_nCurrentActiveTCPSocket;
+
+static struct timeval tv;
+static MILLISECONDS actualtime, lasttime;
+
+/*!\brief handle any connection request coming in the TCP server socket.
+ *
+ */
+void
+checkAndHandleTCPListenerSocket();
+
+/*! \brief check if data has been received on the udp broadcast socket and if yes handle it correctly
+ *
+ */
+void
+checkAndHandleUDPBroadCastSocket();
+
+/*! \brief check if on one of the udp consuming sockets data has been received and if yes handle it correctly
+ *
+ */
+void
+checkAndHandleConsumingUDPSockets();
+/*! \brief check if the given socket is set in the read set
+ *
+ */EIP_BOOL8
+checkSocketSet(int pa_nSocket);
+
+/*!
+ *
+ */
+EIP_STATUS
+handleDataOnTCPSocket(int pa_nSocket);
+
+static MILLISECONDS
+getmilliseconds(void)
+{
+#ifdef WIN32
+  SYSTEMTIME tt;
+  GetSystemTime(&tt);
+  return (MILLISECONDS) tt.wSecond * 1000 + (MILLISECONDS) tt.wMilliseconds;
+#else
+  struct timeval tv;
+  gettimeofday(&tv, 0);
+  return (MILLISECONDS) tv.tv_sec * 1000 + (MILLISECONDS) tv.tv_usec / 1000;
+#endif
+}
+
+/* INT8 Start_NetworkHandler()
+ * 	start a TCP listening socket, accept connections, receive data in select loop, call manageConnections periodically.
+ * 	return status
+ * 			-1 .. error
+ */
+
+struct NetworkStatus
+{
+  int nTCPListener;
+  int nUDPListener;
+  MILLISECONDS elapsedtime;
+} PACKED;
+
+struct NetworkStatus TheNetworkStatus;
+
+EIP_STATUS
+NetworkHandler_Init(void)
+{
+  struct sockaddr_in my_addr;
+  int y;
+  int nOptVal;
+
+#ifdef WIN32
+  WORD wVersionRequested;
+  WSADATA wsaData;
+  wVersionRequested = MAKEWORD(2, 2);
+  WSAStartup(wVersionRequested, &wsaData);
+#endif
+
+  /* clear the master an temp sets                                            */
+  FD_ZERO(&master);
+  FD_ZERO(&read_fds);
+
+  /* create a new TCP socket */
+  if ((TheNetworkStatus.nTCPListener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
+    {
+      OPENER_TRACE_ERR("error allocating socket stream listener, %d\n", errno);
+      return EIP_ERROR;
+    }
+
+  nOptVal = 1;
+  if (setsockopt(TheNetworkStatus.nTCPListener, SOL_SOCKET, SO_REUSEADDR,
+      (char *)&nOptVal, sizeof(nOptVal)) == -1)
+    {
+      OPENER_TRACE_ERR("error setting socket option SO_REUSEADDR on nTCPListener\n");
+      return EIP_ERROR;
+    }
+
+  /* create a new UDP socket */
+  if ((TheNetworkStatus.nUDPListener = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+    {
+      OPENER_TRACE_ERR("error allocating udp listener socket, %d\n", errno);
+      return EIP_ERROR;
+    }
+
+  if (setsockopt(TheNetworkStatus.nUDPListener, SOL_SOCKET, SO_REUSEADDR,
+      (char *)&nOptVal, sizeof(nOptVal)) == -1)
+    {
+      OPENER_TRACE_ERR("error setting socket option SO_REUSEADDR on nUDPListener\n");
+      return EIP_ERROR;
+    }
+
+  my_addr.sin_family = AF_INET;
+  my_addr.sin_port = htons(OPENER_ETHERNET_PORT);
+  my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+  memset(&my_addr.sin_zero, 0, sizeof(my_addr.sin_zero));
+
+  /* bind the new socket to port 0xAF12 (CIP) */
+  if ((bind(TheNetworkStatus.nTCPListener, (struct sockaddr *) &my_addr,
+      sizeof(struct sockaddr))) == -1)
+    {
+      OPENER_TRACE_ERR("error with bind: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+
+  /* enable the udp socket to receive broadcast messages*/
+  y = 1;
+  if (0
+      > setsockopt(TheNetworkStatus.nUDPListener, SOL_SOCKET, SO_BROADCAST, (char *)&y,
+          sizeof(int)))
+    {
+      OPENER_TRACE_ERR("error with setting broadcast receive for udp socket: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+
+  if ((bind(TheNetworkStatus.nUDPListener, (struct sockaddr *) &my_addr,
+      sizeof(struct sockaddr))) == -1)
+    {
+      OPENER_TRACE_ERR("error with udp bind: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+
+  /* switch socket in listen mode */
+  if ((listen(TheNetworkStatus.nTCPListener, MAX_NO_OF_TCP_SOCKETS)) == -1)
+    {
+      OPENER_TRACE_ERR("networkhandler: error with listen: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+
+  /* add the listener socket to the master set */FD_SET(
+      TheNetworkStatus.nTCPListener, &master);
+  FD_SET(TheNetworkStatus.nUDPListener, &master);
+
+  /* keep track of the biggest file descriptor */
+  fdmax =
+      (TheNetworkStatus.nTCPListener > TheNetworkStatus.nUDPListener) ? TheNetworkStatus.nTCPListener :
+          TheNetworkStatus.nUDPListener;
+
+  lasttime = getmilliseconds(); /* initialize time keeping */
+  TheNetworkStatus.elapsedtime = 0;
+
+  return EIP_OK;
+}
+
+EIP_STATUS
+NetworkHandler_ProcessOnce(void)
+{
+  int fd;
+  int res;
+
+  read_fds = master;
+
+  tv.tv_sec = 0;
+  tv.tv_usec = (
+      TheNetworkStatus.elapsedtime < OPENER_TIMER_TICK ? OPENER_TIMER_TICK
+          - TheNetworkStatus.elapsedtime :
+          0) * 1000; /* 10 ms */
+
+  res = select(fdmax + 1, &read_fds, 0, 0, &tv);
+  if (res == -1)
+    {
+      if (EINTR == errno) /* we have somehow been interrupted. The default behavior is to go back into the select loop. */
+        {
+          return EIP_OK;
+        }
+      else
+        {
+          OPENER_TRACE_ERR("networkhandler: error with select: %s\n", strerror(errno));
+          return EIP_ERROR;
+        }
+    }
+
+  if (res > 0)
+    {
+
+      checkAndHandleTCPListenerSocket();
+      checkAndHandleUDPBroadCastSocket();
+      checkAndHandleConsumingUDPSockets();
+
+      for (fd = 0; fd <= fdmax; fd++)
+        {
+          if (true == checkSocketSet(fd))
+            {
+              /* if it is still checked it is a TCP receive */
+              if (EIP_ERROR == handleDataOnTCPSocket(fd)) /* if error */
+                {
+                  IApp_CloseSocket(fd);
+                  closeSession(fd); /* clean up session and close the socket */
+                }
+            }
+        }
+    }
+
+  actualtime = getmilliseconds();
+  TheNetworkStatus.elapsedtime += actualtime - lasttime;
+  lasttime = actualtime;
+
+  /* check if we had been not able to update the connection manager for several OPENER_TIMER_TICK.
+   * This should compensate the jitter of the windows timer
+   */
+  while (TheNetworkStatus.elapsedtime >= OPENER_TIMER_TICK)
+    {
+      /* call manage_connections() in connection manager every OPENER_TIMER_TICK ms */
+      manageConnections();
+      TheNetworkStatus.elapsedtime -= OPENER_TIMER_TICK;
+    }
+  return EIP_OK;
+}
+
+EIP_STATUS
+NetworkHandler_Finish(void)
+{
+  IApp_CloseSocket(TheNetworkStatus.nTCPListener);
+  IApp_CloseSocket(TheNetworkStatus.nUDPListener);
+  return EIP_OK;
+}
+
+EIP_BOOL8
+checkSocketSet(int pa_nSocket)
+{
+  EIP_BOOL8 nRetVal = false;
+  if (FD_ISSET(pa_nSocket, &read_fds))
+    {
+      if (FD_ISSET(pa_nSocket, &master))
+        {
+          nRetVal = true;
+        }
+      else
+        {
+          OPENER_TRACE_INFO("socket: %d closed with pending message\n", pa_nSocket);
+        }
+      FD_CLR(pa_nSocket, &read_fds);
+      /* remove it from the read set so that later checks will not find it */
+    }
+
+  return nRetVal;
+}
+EIP_STATUS
+IApp_SendUDPData(struct sockaddr_in *pa_pstAddr, int pa_nSockFd,
+    EIP_UINT8 *pa_acData, EIP_UINT16 pa_nDataLength)
+{
+  int sentlength;
+
+  sentlength = sendto(pa_nSockFd, (char *) pa_acData, pa_nDataLength, 0,
+      (struct sockaddr *) pa_pstAddr, sizeof(*pa_pstAddr));
+
+  if (sentlength < 0)
+    {
+      OPENER_TRACE_ERR("networkhandler: error with sendto in sendUDPData: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+  else if (sentlength != pa_nDataLength)
+    {
+      OPENER_TRACE_WARN("not all data was sent in sendUDPData, sent %d of %d\n",
+          sentlength, pa_nDataLength);
+      return EIP_ERROR;
+    }
+  else
+    return EIP_OK;
+}
+
+EIP_STATUS
+handleDataOnTCPSocket(int pa_nSocket)
+{
+  EIP_UINT8 *rxp;
+  long nCheckVal;
+  size_t unDataSize;
+  long nDataSent;
+  int nRemainingBytes = 0;
+
+  /* We will handle just one EIP packet here the rest is done by the select
+   * method which will inform us if more data is available in the socket
+   because of the current implementation of the main loop this may not be
+   the fastest way and a loop here with a non blocking socket would better
+   fit*/
+
+  /*Check how many data is here -- read the first four bytes from the connection */
+  nCheckVal = recv(pa_nSocket, g_acPCEthernetCommBuffer, 4, 0); /*TODO we may have to set the socket to a non blocking socket */
+
+  if (nCheckVal == 0)
+    {
+      OPENER_TRACE_ERR("networkhandler: connection closed by client: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+  if (nCheckVal < 0)
+    {
+      OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+
+  rxp = &g_acPCEthernetCommBuffer[2]; /* at this place EIP stores the data length */
+  unDataSize = ltohs(&rxp) + ENCAPSULATION_HEADER_LENGTH - 4; /* -4 is for the 4 bytes we have already read*/
+  /* (NOTE this advances the buffer pointer) */
+  if (PC_OPENER_ETHERNET_BUFFER_SIZE - 4 < unDataSize)
+    { /*TODO can this be handled in a better way?*/
+      OPENER_TRACE_ERR("too large packet received will be ignored, will drop the data\n");
+      /* Currently we will drop the whole packet */
+      nDataSent = PC_OPENER_ETHERNET_BUFFER_SIZE;
+
+      do
+        {
+          nCheckVal = recv(pa_nSocket, g_acPCEthernetCommBuffer, nDataSent, 0);
+
+          if (nCheckVal == 0) /* got error or connection closed by client */
+            {
+              OPENER_TRACE_ERR("networkhandler: connection closed by client: %s\n", strerror(errno));
+              return EIP_ERROR;
+            }
+          if (nCheckVal < 0)
+            {
+              OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno));
+              return EIP_ERROR;
+            }
+          unDataSize -= nCheckVal;
+          if ((unDataSize < PC_OPENER_ETHERNET_BUFFER_SIZE)
+              && (unDataSize != 0))
+            {
+              nDataSent = unDataSize;
+            }
+        }
+      while (0 != unDataSize); /*TODO fragile end statement */
+      return EIP_OK;
+    }
+
+  nCheckVal = recv(pa_nSocket, &g_acPCEthernetCommBuffer[4], unDataSize, 0);
+
+  if (nCheckVal == 0) /* got error or connection closed by client */
+    {
+      OPENER_TRACE_ERR("networkhandler: connection closed by client: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+  if (nCheckVal < 0)
+    {
+      OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno));
+      return EIP_ERROR;
+    }
+
+  if ((unsigned) nCheckVal == unDataSize)
+    {
+      /*we got the right amount of data */
+      unDataSize += 4;
+      /*TODO handle partial packets*/
+      OPENER_TRACE_INFO("Data received on tcp:\n");
+
+      g_nCurrentActiveTCPSocket = pa_nSocket;
+
+      nCheckVal = handleReceivedExplictTCPData(pa_nSocket,
+          g_acPCEthernetCommBuffer, unDataSize, &nRemainingBytes);
+
+      g_nCurrentActiveTCPSocket = -1;
+
+      if (nRemainingBytes != 0)
+        {
+          OPENER_TRACE_WARN("Warning: received packet was to long: %d Bytes left!\n",
+              nRemainingBytes);
+        }
+
+      if (nCheckVal > 0)
+        {
+          OPENER_TRACE_INFO("reply sent:\n");
+
+          nDataSent = send(pa_nSocket, (char *) g_acPCEthernetCommBuffer,
+              nCheckVal, 0);
+          if (nDataSent != nCheckVal)
+            {
+              OPENER_TRACE_WARN("TCP response was not fully sent\n");
+            }
+        }
+
+      return EIP_OK;
+    }
+  else
+    {
+      /* we got a fragmented packet currently we cannot handle this will
+       * for this we would need a network buffer per TCP socket
+       *
+       * However with typical packet sizes of EIP this should't be a big issue.
+       */
+      /*TODO handle fragmented packets */
+    }
+  return EIP_ERROR;
+}
+
+/* create a new UDP socket for the connection manager
+ returns the fd if successful, else -1 */
+int
+IApp_CreateUDPSocket(int pa_nDirection, struct sockaddr_in *pa_pstAddr)
+{
+  struct sockaddr_in stPeerAdr;
+  int newfd;
+#ifdef WIN32
+  unsigned long nPeerAddrLen;
+#else
+  socklen_t nPeerAddrLen;
+#endif
+
+  nPeerAddrLen = sizeof(struct sockaddr_in);
+  /* create a new UDP socket */
+  if ((newfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+    {
+      OPENER_TRACE_ERR("networkhandler: cannot create UDP socket: %s\n", strerror(errno));
+      return EIP_INVALID_SOCKET;
+    }
+
+  OPENER_TRACE_INFO("networkhandler: UDP socket %d\n", newfd);
+
+  /* check if it is sending or receiving */
+  if (pa_nDirection == CONSUMING)
+    {
+      int nOptVal = 1;
+      if (setsockopt(newfd, SOL_SOCKET, SO_REUSEADDR, (char *)&nOptVal, sizeof(nOptVal))
+          == -1)
+        {
+          OPENER_TRACE_ERR("error setting socket option SO_REUSEADDR on consuming udp socket\n");
+          return EIP_ERROR;
+        }
+
+      /* bind is only for consuming necessary */
+      if ((bind(newfd, (struct sockaddr *) pa_pstAddr, sizeof(struct sockaddr)))
+          == -1)
+        {
+          OPENER_TRACE_ERR("error on bind udp: %s\n", strerror(errno));
+          return EIP_INVALID_SOCKET;
+        }
+
+      OPENER_TRACE_INFO("networkhandler: bind UDP socket %d\n", newfd);
+    }
+  else
+    { /* we have a producing udp socket */
+
+      if (pa_pstAddr->sin_addr.s_addr == g_stMultiCastconfig.m_unMcastStartAddr)
+        {
+          if (1 != g_unTTLValue)
+            { /* we need to set a TTL value for the socket */
+              if (setsockopt(newfd, IPPROTO_IP, IP_MULTICAST_TTL,
+                  &g_unTTLValue, sizeof(g_unTTLValue) < 0))
+                {
+                  OPENER_TRACE_ERR("networkhandler: could not set the TTL to: %d, error: %s\n", g_unTTLValue, strerror(errno));
+                  return EIP_INVALID_SOCKET;
+                }
+            }
+        }
+    }
+
+  if ((pa_nDirection == CONSUMING) || (0 == pa_pstAddr->sin_addr.s_addr))
+    {
+      /* we have a peer to peer producer or a consuming connection*/
+      if (getpeername(g_nCurrentActiveTCPSocket, (struct sockaddr *) &stPeerAdr,
+          &nPeerAddrLen) < 0)
+        {
+          OPENER_TRACE_ERR("networkhandler: could not get peername: %s\n", strerror(errno));
+          return EIP_INVALID_SOCKET;
+        }
+      /* store the originators address */
+      pa_pstAddr->sin_addr.s_addr = stPeerAdr.sin_addr.s_addr;
+    }
+
+  /* add new fd to the master list                                             */
+  FD_SET(newfd, &master);
+  if (newfd > fdmax)
+    {
+      fdmax = newfd;
+    }
+  return newfd;
+}
+
+void
+IApp_CloseSocket(int pa_nSockFd)
+{
+
+  OPENER_TRACE_INFO("networkhandler: closing socket %d\n", pa_nSockFd);
+  if (EIP_INVALID_SOCKET != pa_nSockFd)
+    {
+      FD_CLR(pa_nSockFd, &master);
+#ifdef WIN32
+      closesocket(pa_nSockFd);
+#else
+      shutdown(pa_nSockFd, SHUT_RDWR);
+      close(pa_nSockFd);
+#endif
+    }
+}
+
+void
+checkAndHandleTCPListenerSocket()
+{
+  int newfd;
+  /* see if this is a connection request to the TCP listener*/
+  if (true == checkSocketSet(TheNetworkStatus.nTCPListener))
+    {
+      OPENER_TRACE_INFO("networkhandler: new TCP connection\n");
+
+      newfd = accept(TheNetworkStatus.nTCPListener, NULL, NULL);
+      if (newfd == -1)
+        {
+          OPENER_TRACE_ERR("networkhandler: error on accept: %s\n", strerror(errno));
+          return;
+        }
+
+      FD_SET(newfd, &master);
+      /* add newfd to master set */
+      if (newfd > fdmax)
+        {
+          fdmax = newfd;
+        }
+
+      OPENER_TRACE_STATE(
+          "networkhandler: opened new TCP connection on fd %d\n",
+          newfd);
+    }
+}
+
+void
+checkAndHandleUDPBroadCastSocket()
+{
+  int nReceived_size;
+  int nRemainingBytes;
+  int nReplyLen;
+  EIP_UINT8 *rxp;
+  struct sockaddr_in stFrom;
+#ifndef WIN32
+  socklen_t nFromLen;
+#else
+  unsigned long nFromLen;
+#endif
+
+  /* see if this is an unsolicited inbound UDP message */
+  if (true == checkSocketSet(TheNetworkStatus.nUDPListener))
+    {
+
+      nFromLen = sizeof(stFrom);
+
+      OPENER_TRACE_STATE(
+          "networkhandler: unsolicited UDP message on EIP broadcast socket\n");
+
+      /*Handle udp broadcast messages */
+      nReceived_size = recvfrom(TheNetworkStatus.nUDPListener,
+          g_acPCEthernetCommBuffer, PC_OPENER_ETHERNET_BUFFER_SIZE, 0,
+          (struct sockaddr *) &stFrom, &nFromLen);
+
+      if (nReceived_size <= 0)
+        { /* got error */
+          OPENER_TRACE_ERR(
+              "networkhandler: error on recvfrom udp broadcast port: %s\n",
+              strerror(errno));
+          return;
+        }
+
+      OPENER_TRACE_INFO("Data received on udp:\n");
+
+      rxp = &g_acPCEthernetCommBuffer[0];
+      do
+        {
+          nReplyLen = handleReceivedExplictUDPData(
+              TheNetworkStatus.nUDPListener, &stFrom, rxp, nReceived_size,
+              &nRemainingBytes);
+
+          rxp += nReceived_size - nRemainingBytes;
+          nReceived_size = nRemainingBytes;
+
+          if (nReplyLen > 0)
+            {
+              OPENER_TRACE_INFO("reply sent:\n");
+
+              /* if the active fd matches a registered UDP callback, handle a UDP packet */
+              if (sendto(TheNetworkStatus.nUDPListener,
+                  (char *) g_acPCEthernetCommBuffer, nReplyLen, 0,
+                  (struct sockaddr *) &stFrom, sizeof(stFrom)) != nReplyLen)
+                {
+                  OPENER_TRACE_INFO(
+                      "networkhandler: UDP response was not fully sent\n");
+                }
+            }
+        }
+      while (nRemainingBytes > 0);
+    }
+}
+
+void
+checkAndHandleConsumingUDPSockets()
+{
+  int nReceived_size;
+  struct sockaddr_in stFrom;
+#ifndef WIN32
+  socklen_t nFromLen;
+#else
+  unsigned long nFromLen;
+#endif
+
+  S_CIP_ConnectionObject *pstRunner = g_pstActiveConnectionList;
+  S_CIP_ConnectionObject *pstCurrent;
+
+  /* see a message on one of the registered UDP sockets has been received     */
+  while (NULL != pstRunner)
+    {
+      pstCurrent = pstRunner;
+      pstRunner = pstRunner->m_pstNext; /* do this at the beginning as the close function may can make the entry invalid */
+
+      if ((-1 != pstCurrent->sockfd[CONSUMING])
+          && (true == checkSocketSet(pstCurrent->sockfd[CONSUMING])))
+        {
+          nFromLen = sizeof(stFrom);
+          nReceived_size = recvfrom(pstCurrent->sockfd[CONSUMING],
+              g_acPCEthernetCommBuffer, PC_OPENER_ETHERNET_BUFFER_SIZE, 0,
+              (struct sockaddr *) &stFrom, &nFromLen);
+          if (0 == nReceived_size)
+            {
+              OPENER_TRACE_STATE("connection closed by client\n");
+              pstCurrent->m_pfCloseFunc(pstCurrent);
+              continue;
+            }
+
+          if (0 > nReceived_size)
+            {
+              OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno));
+              pstCurrent->m_pfCloseFunc(pstCurrent);
+              continue;
+            }
+
+          handleReceivedConnectedData(g_acPCEthernetCommBuffer, nReceived_size,
+              &stFrom);
+
+        }
+    }
+}

+ 20 - 0
src/ports/WIN32/networkhandler.h

@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2009, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+#ifndef NETWORKHANDLER_H_
+#define NETWORKHANDLER_H_
+
+#include "typedefs.h"
+
+
+/*! Start a TCP/UDP listening socket, accept connections, receive data in select loop, call manageConnections periodically.
+ *  @return status
+ *          EIP_ERROR .. error
+ */
+EIP_STATUS NetworkHandler_Init(void);
+EIP_STATUS NetworkHandler_ProcessOnce(void);
+EIP_STATUS NetworkHandler_Finish(void);
+
+#endif /*NETWORKHANDLER_H_*/

+ 10 - 0
src/ports/WIN32/sample_application/CMakeLists.txt

@@ -0,0 +1,10 @@
+set( SRC_DIR "${PROJECT_SOURCE_DIR}/src" )
+set( CIP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/cip" )
+set( ENET_ENCAP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/enet_encap" )
+set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
+
+include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
+
+opener_platform_support("INCLUDES")
+
+add_library(SAMPLE_APP sampleapplication.c)

+ 175 - 0
src/ports/WIN32/sample_application/opener_user_conf.h

@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * 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
+ */
+#ifdef WIN32
+#include <windows.h>
+typedef unsigned short in_port_t;
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#endif
+
+/*! Identity configuration of the device */
+#define OPENER_DEVICE_VENDOR_ID           1
+#define OPENER_DEVICE_TYPE               12
+#define OPENER_DEVICE_PRODUCT_CODE      65001
+#define OPENER_DEVICE_MAJOR_REVISION      1
+#define OPENER_DEVICE_MINOR_REVISION      2
+#define OPENER_DEVICE_NAME      "OpENer PC"
+
+/*! 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
+
+/*! 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
+
+/*! 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
+
+/*! 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
+
+/*! Define the number of supported input only connections per connection path
+ */
+#define OPENER_CIP_NUM_INPUT_ONLY_CONNS_PER_CON_PATH 3
+
+
+/*! 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
+
+/*! Define the number of supported Listen only connections per connection path
+ */
+#define OPENER_CIP_NUM_LISTEN_ONLY_CONNS_PER_CON_PATH   3
+
+
+/*! 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
+
+/*! Number of sessions that can be handled at the same time
+ */ 
+#define OPENER_NUMBER_OF_SUPPORTED_SESSIONS 20
+
+/*! The time in ms of the timer used in this implementations
+ */ 
+#define OPENER_TIMER_TICK 10 
+
+
+/*! Define if RUN IDLE data is sent with consumed data
+ */ 
+#define OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER 1
+
+/*! Define if RUN IDLE data is to be sent with produced data
+ *
+ * Per default we don't send run idle headers with produced data
+ */
+#define OPENER_PRODUCED_DATA_HAS_RUN_IDLE_HEADER 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);*/
+
+
+/*! 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
+
+/*! 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
+
+
+/*! If this define is here opener will enable 64Bit data type support.
+ *
+ */
+#define OPENER_SUPPORT_64BIT_DATATYPES 1
+
+#endif /*OPENER_USER_CONF_H_*/

+ 162 - 0
src/ports/WIN32/sample_application/sampleapplication.c

@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2012, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include "opener_api.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               151 //0x097
+#define DEMO_APP_HEARBEAT_INPUT_ONLY_ASSEMBLY_NUM  152 //0x098
+#define DEMO_APP_HEARBEAT_LISTEN_ONLY_ASSEMBLY_NUM 153 //0x099
+#define DEMO_APP_EXPLICT_ASSEMBLY_NUM              154 //0x09A
+
+/* global variables for demo application (4 assembly data fields)  ************/
+
+EIP_UINT8 g_assemblydata064[32]; /* Input */
+EIP_UINT8 g_assemblydata096[32]; /* Output */
+EIP_UINT8 g_assemblydata097[10]; /* Config */
+EIP_UINT8 g_assemblydata09A[32]; /* Explicit */
+
+EIP_STATUS
+IApp_Init(void)
+{
+  /* create 3 assembly object instances*/
+  /*INPUT*/
+  createAssemblyObject(DEMO_APP_INPUT_ASSEMBLY_NUM, &g_assemblydata064[0],
+      sizeof(g_assemblydata064));
+
+  /*OUTPUT*/
+  createAssemblyObject(DEMO_APP_OUTPUT_ASSEMBLY_NUM, &g_assemblydata096[0],
+      sizeof(g_assemblydata096));
+
+  /*CONFIG*/
+  createAssemblyObject(DEMO_APP_CONFIG_ASSEMBLY_NUM, &g_assemblydata097[0],
+      sizeof(g_assemblydata097));
+
+  /*Heart-beat output assembly for Input only connections */
+  createAssemblyObject(DEMO_APP_HEARBEAT_INPUT_ONLY_ASSEMBLY_NUM, 0, 0);
+
+  /*Heart-beat output assembly for Listen only connections */
+  createAssemblyObject(DEMO_APP_HEARBEAT_LISTEN_ONLY_ASSEMBLY_NUM, 0, 0);
+
+  /* assembly for explicit messaging */
+  createAssemblyObject(DEMO_APP_EXPLICT_ASSEMBLY_NUM, &g_assemblydata09A[0],
+      sizeof(g_assemblydata09A));
+
+  configureExclusiveOwnerConnectionPoint(0, DEMO_APP_OUTPUT_ASSEMBLY_NUM,
+      DEMO_APP_INPUT_ASSEMBLY_NUM, DEMO_APP_CONFIG_ASSEMBLY_NUM);
+  configureInputOnlyConnectionPoint(0,
+      DEMO_APP_HEARBEAT_INPUT_ONLY_ASSEMBLY_NUM, DEMO_APP_INPUT_ASSEMBLY_NUM,
+      DEMO_APP_CONFIG_ASSEMBLY_NUM);
+  configureListenOnlyConnectionPoint(0,
+      DEMO_APP_HEARBEAT_LISTEN_ONLY_ASSEMBLY_NUM, DEMO_APP_INPUT_ASSEMBLY_NUM,
+      DEMO_APP_CONFIG_ASSEMBLY_NUM);
+
+  return EIP_OK;
+}
+
+void
+IApp_HandleApplication(void)
+{
+  /* check if application needs to trigger an connection */
+}
+
+void
+IApp_IOConnectionEvent(unsigned int pa_unOutputAssembly,
+    unsigned int pa_unInputAssembly, EIOConnectionEvent 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 */
+}
+
+EIP_STATUS
+IApp_AfterAssemblyDataReceived(S_CIP_Instance *pa_pstInstance)
+{
+  EIP_STATUS nRetVal = EIP_OK;
+
+  /*handle the data received e.g., update outputs of the device */
+  switch (pa_pstInstance->nInstanceNr)
+    {
+  case DEMO_APP_OUTPUT_ASSEMBLY_NUM:
+    /* Data for the output assembly has been received.
+     * Mirror it to the inputs */
+    memcpy(&g_assemblydata064[0], &g_assemblydata096[0],
+        sizeof(g_assemblydata064));
+    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 = EIP_OK;
+    break;
+    }
+  return nRetVal;
+}
+
+EIP_BOOL8
+IApp_BeforeAssemblyDataSend(S_CIP_Instance *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->nInstanceNr == DEMO_APP_EXPLICT_ASSEMBLY_NUM)
+    {
+      /* do something interesting with the existing data
+       * for the explicit get-data-attribute message */
+    }
+  return true;
+}
+
+EIP_STATUS
+IApp_ResetDevice(void)
+{
+  /* add reset code here*/
+  return EIP_OK;
+}
+
+EIP_STATUS
+IApp_ResetDeviceToInitialConfiguration(void)
+{
+  /*rest the parameters */
+
+  /*than perform device reset*/
+  IApp_ResetDevice();
+  return EIP_OK;
+}
+
+void *
+IApp_CipCalloc(unsigned pa_nNumberOfElements, unsigned pa_nSizeOfElement)
+{
+  return calloc(pa_nNumberOfElements, pa_nSizeOfElement);
+}
+
+void
+IApp_CipFree(void *pa_poData)
+{
+  free(pa_poData);
+}
+
+void
+IApp_RunIdleChanged(EIP_UINT32 pa_nRunIdleValue)
+{
+  (void) pa_nRunIdleValue;
+}
+

+ 0 - 17
src/ports/platform-pc/sample_application/CMakeLists.txt

@@ -1,17 +0,0 @@
-set( SRC_DIR "${PROJECT_SOURCE_DIR}/src" )
-set( CIP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/cip" )
-set( ENET_ENCAP_SRC_DIR "${PROJECT_SOURCE_DIR}/src/enet_encap" )
-set( PORTS_SRC_DIR "${PROJECT_SOURCE_DIR}/src/ports")
-
-include_directories( ${PROJECT_SOURCE_DIR} ${SRC_DIR} ${CIP_SRC_DIR} ${ENET_ENCAP_SRC_DIR} ${PORTS_SRC_DIR} )
-
-if(PLATFORM_POSIX)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application)
-elseif(PLATFORM_WIN32)
-  include_directories(${PORTS_SRC_DIR}/platform-pc ${PORTS_SRC_DIR}/platform-pc/sample_application ${MSINTTYPES_INCLUDES})
-  set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32" )
-else(PLATFORM_POSIX)
-  message(FATAL_ERROR "No platform selected!")
-endif(PLATFORM_POSIX)
-
-add_library(SAMPLE_APP sampleapplication.c)