Просмотр исходного кода

Adds Forward Open data structure and functions

CapXilinx 9 лет назад
Родитель
Сommit
a67eaec328

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

@@ -12,6 +12,8 @@ opener_common_includes()
 #######################################
 opener_platform_support("INCLUDES")
 
+add_subdirectory( connectionmanager )
+
 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 cipepath.h cipepath.c cipelectronickey.h cipelectronickey.c )
 
 add_library( CIP ${CIP_SRC} )

+ 9 - 9
source/src/cip/cipconnectionobject.h

@@ -43,15 +43,15 @@ typedef enum {
 } CipConnectionObjectTransportClassTriggerProductionTrigger;
 
 typedef enum {
-  kCipConnectionObjectTransportClassInvalid = -1,
-  kCipConnectionObjectTransportClass0 = 0,
-  kCipConnectionObjectTransportClass1 = 1,
-  kCipConnectionObjectTransportClass2 = 2,
-  kCipConnectionObjectTransportClass3 = 3,
-  kCipConnectionObjectTransportClass4 = 4,
-  kCipConnectionObjectTransportClass5 = 5,
-  kCipConnectionObjectTransportClass6 = 6
-} CipConnectionObjectTransportClass;
+  kCipConnectionObjectTransportClassTriggerClassInvalid = -1,
+  kCipConnectionObjectTransportClassTriggerClass0 = 0,
+  kCipConnectionObjectTransportClassTriggerClass1 = 1,
+  kCipConnectionObjectTransportClassTriggerClass2 = 2,
+  kCipConnectionObjectTransportClassTriggerClass3 = 3,
+  kCipConnectionObjectTransportClassTriggerClass4 = 4,
+  kCipConnectionObjectTransportClassTriggerClass5 = 5,
+  kCipConnectionObjectTransportClassTriggerClass6 = 6
+} CipConnectionObjectTransportClassTriggerClass;
 
 typedef enum {
   kCipConnectionObjectWatchdogTimeoutActionTransitionToTimedOut = 0,

+ 12 - 12
source/src/cip/cipioconnection.c

@@ -104,21 +104,21 @@ EipUint16 ProcessProductionInhibitTime(ConnectionObject *io_connection_object) {
   return kConnectionManagerExtendedStatusCodeSuccess;
 }
 
-CipConnectionObjectTransportClass GetConnectionTransportClass(
+CipConnectionObjectTransportClassTriggerClass GetConnectionTransportClass(
   const ConnectionObject *const connection_object) {
   const unsigned int kTransportClassMask = 0x0F;
 
   switch(connection_object->transport_type_class_trigger &
          kTransportClassMask) {
-    case 0: return kCipConnectionObjectTransportClass0;
-    case 1: return kCipConnectionObjectTransportClass1;
-    case 2: return kCipConnectionObjectTransportClass2;
-    case 3: return kCipConnectionObjectTransportClass3;
-    case 4: return kCipConnectionObjectTransportClass4;
-    case 5: return kCipConnectionObjectTransportClass5;
-    case 6: return kCipConnectionObjectTransportClass6;
-  }
-  return kCipConnectionObjectTransportClassInvalid;
+    case 0: return kCipConnectionObjectTransportClassTriggerClass0;
+    case 1: return kCipConnectionObjectTransportClassTriggerClass1;
+    case 2: return kCipConnectionObjectTransportClassTriggerClass2;
+    case 3: return kCipConnectionObjectTransportClassTriggerClass3;
+    case 4: return kCipConnectionObjectTransportClassTriggerClass4;
+    case 5: return kCipConnectionObjectTransportClassTriggerClass5;
+    case 6: return kCipConnectionObjectTransportClassTriggerClass6;
+  }
+  return kCipConnectionObjectTransportClassTriggerClassInvalid;
 }
 
 void SetIoConnectionCallbacks(ConnectionObject *const io_connection_object) {
@@ -157,7 +157,7 @@ EipUint16 SetupIoConnectionOriginatorToTargetConnectionPoint(
     CipAttributeStruct *attribute = GetCipAttribute(instance, 3);
     OPENER_ASSERT(attribute != NULL);
     int is_heartbeat = ( ( (CipByteArray *) attribute->data )->length == 0 );
-    if ( kCipConnectionObjectTransportClass1
+    if ( kCipConnectionObjectTransportClassTriggerClass1
          == GetConnectionTransportClass(io_connection_object) ) {
       //if ((io_connection_object->transport_type_class_trigger & 0x0F) == 1) {
       /* class 1 connection */
@@ -209,7 +209,7 @@ EipUint16 SetupIoConnectionTargetToOriginatorConnectionPoint(
     CipAttributeStruct *attribute = GetCipAttribute(instance, 3);
     OPENER_ASSERT(attribute != NULL);
     int is_heartbeat = ( ( (CipByteArray *) attribute->data )->length == 0 );
-    if ( kCipConnectionObjectTransportClass1 ==
+    if ( kCipConnectionObjectTransportClassTriggerClass1 ==
          GetConnectionTransportClass(io_connection_object) ) {
       /* class 1 connection */
       data_size -= 2; /* remove 16-bit sequence count length */

+ 17 - 0
source/src/cip/connectionmanager/CMakeLists.txt

@@ -0,0 +1,17 @@
+#######################################
+# CIP connection manager library      #
+#######################################
+
+#######################################
+# Add common includes                 #
+#######################################
+opener_common_includes()
+
+#######################################
+# Add platform-specific includes      #
+#######################################
+opener_platform_support("INCLUDES")
+
+set( CIP_CONNECTION_MANAGER_SRC cipforwardopen.c )
+
+add_library( CIP_CONNECTION_MANAGER ${CIP_CONNECTION_MANAGER_SRC} )

+ 393 - 0
source/src/cip/connectionmanager/cipforwardopen.c

@@ -0,0 +1,393 @@
+/*******************************************************************************
+ * Copyright (c) 2016, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include "cipforwardopen.h"
+
+#include "endianconv.h"
+
+typedef struct cip_forward_open_data {
+  CipByte priority_time_tick;
+  CipUsint timeout_ticks;
+  CipUdint originiator_to_target_network_connection_id;
+  CipUdint target_to_originator_network_connection_id;
+  CipUint connection_serial_number;
+  CipUint originator_vendor_id;
+  CipUdint originator_serial_number;
+  CipUsint connection_timeout_multiplier;
+  CipUdint originator_to_target_requested_packet_interval;
+  CipWord originator_to_target_network_connection_parameters;
+  CipUdint target_to_originator_requested_packet_interval;
+  CipWord target_to_originator_network_connection_parameters;
+  CipByte transport_class_and_trigger;
+  CipUsint connection_path_size;
+  const CipOctet *connection_path;
+} CipForwardOpenData;
+
+const size_t CipForwardOpenDataSize = sizeof(CipForwardOpenData);
+
+const CipOctet *CipForwardOpenGetForwardOpenDataFromMessage(
+  CipForwardOpenData *const data, const CipOctet **const message) {
+  const CipOctet **message_runner = message;
+  data->priority_time_tick = GetByteFromMessage(message_runner);
+  data->timeout_ticks = GetUsintFromMessage(message_runner);
+  data->originiator_to_target_network_connection_id = GetUdintFromMessage(
+    message_runner);
+  data->target_to_originator_network_connection_id = GetUdintFromMessage(
+    message_runner);
+  data->connection_serial_number = GetUintFromMessage(message_runner);
+  data->originator_vendor_id = GetUintFromMessage(message_runner);
+  data->originator_serial_number = GetUdintFromMessage(message_runner);
+  data->connection_timeout_multiplier = GetUsintFromMessage(message_runner);
+  *message_runner += 3; //Move pointer over the reserved space
+  data->originator_to_target_requested_packet_interval = GetUdintFromMessage(
+    message_runner);
+  data->originator_to_target_network_connection_parameters = GetWordFromMessage(
+    message_runner);
+  data->target_to_originator_requested_packet_interval = GetUdintFromMessage(
+    message_runner);
+  data->target_to_originator_network_connection_parameters = GetWordFromMessage(
+    message_runner);
+  data->transport_class_and_trigger = GetByteFromMessage(message_runner);
+  data->connection_path_size = GetUsintFromMessage(message_runner);
+  data->connection_path = *message_runner;
+  *message_runner = *message_runner + data->connection_path_size *
+                    sizeof(CipOctet);
+  return *message_runner;
+}
+
+CipForwardOpenPriorityTickTime CipForwardOpenGetPriorityTickTime(
+  CipForwardOpenData *forward_open_data) {
+  const CipByte kPriorityMask = 0x10;
+  if( kPriorityMask ==
+      ( (forward_open_data->priority_time_tick) & kPriorityMask ) ) {
+    return kCipForwardOpenPriorityTickTimeReserved;
+  }
+  else {
+    return kCipForwardOpenPriorityTickTimeNormal;
+  }
+}
+
+uint8_t CipForwardOpenGetTickTime(CipForwardOpenData *forward_open_data) {
+  const CipByte kTickTimeMask = 0x0F;
+  return (forward_open_data->priority_time_tick & kTickTimeMask);
+}
+
+CipUsint CipForwardOpenGetTimeoutTick(CipForwardOpenData *forward_open_data) {
+  return forward_open_data->timeout_ticks;
+}
+
+MilliSeconds CipForwardOpenGetTimeoutInMilliseconds(
+  CipForwardOpenData *forward_open_data) {
+  return CipForwardOpenGetTimeoutTick(forward_open_data) * 1 <<
+    CipForwardOpenGetTickTime(forward_open_data);
+}
+
+CipUdint CipForwardOpenGetOriginatorToTargetNetworkConnectionId(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->originiator_to_target_network_connection_id;
+}
+
+CipUdint CipForwardOpenGetTargetToOriginatorNetworkConnectionId(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->target_to_originator_network_connection_id;
+}
+
+CipUint CipForwardOpenGetConnectionSerialNumber(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->connection_serial_number;
+}
+
+CipUint CipForwardOpenGetOriginatorVendorId(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->originator_vendor_id;
+}
+
+CipUdint CipForwardOpenGetOriginatorSerialNumber(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->originator_serial_number;
+}
+
+uint16_t CipForwardOpenGetTimeoutMultiplier(
+  CipForwardOpenData *forward_open_data) {
+  if (8 > forward_open_data->connection_timeout_multiplier) {
+    return 4 << forward_open_data->connection_timeout_multiplier;
+  }
+
+  return 0;
+}
+
+MicroSeconds CipForwardOpenGetOriginatorToTargetRequestedPacketInterval(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->originator_to_target_requested_packet_interval;
+}
+
+CipWord CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->originator_to_target_network_connection_parameters;
+}
+
+static bool GetRedundantOwner(const CipWord connection_parameters) {
+  const CipWord kRedundantOwnerMask = 0x8000;
+  if ( kRedundantOwnerMask == (kRedundantOwnerMask & connection_parameters) ) {
+    return true;
+  }
+  return false;
+}
+
+bool CipForwardOpenGetOriginatorToTargetRedundantOwner(
+  CipForwardOpenData *forward_open_data) {
+  return GetRedundantOwner( CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+                              forward_open_data) );
+}
+
+static CipForwardOpenConnectionType GetConnectionType(
+  CipWord connection_parameters) {
+#define NULL_CONNECTION_TYPE 0x0000
+#define MULTICAST_CONNECTION_TYPE 0x2000
+#define POINT_TO_POINT_CONNECTION_TYPE 0x4000
+
+  const CipWord kConnectionTypeMask = 0x6000;
+  const CipWord kConnectionType = kConnectionTypeMask & connection_parameters;
+
+  CipForwardOpenConnectionType type = kCipForwardOpenConnectionTypeReserved;
+
+  switch(kConnectionType) {
+    case NULL_CONNECTION_TYPE: type = kCipForwardOpenConnectionTypeNull; break;
+    case MULTICAST_CONNECTION_TYPE: type =
+      kCipForwardOpenConnectionTypeMulticastConnection; break;
+    case POINT_TO_POINT_CONNECTION_TYPE: type =
+      kCipForwardOpenConnectionTypePointToPointConnection; break;
+    default: type = kCipForwardOpenConnectionTypeReserved; break;
+  }
+
+  return type;
+
+#undef NULL_CONNECTION_TYPE
+#undef MULTICAST_CONNECTION_TYPE
+#undef POINT_TO_POINT_CONNECTION_TYPE
+}
+
+CipForwardOpenConnectionType CipForwardOpenGetOriginatorToTargetConnectionType(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionType( CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+                              forward_open_data) );
+}
+
+static CipForwardOpenConnectionPriority GetConnectionPriority(
+  const CipWord connection_parameters) {
+#define LOW_PRIORITY 0x0000
+#define HIGH_PRIORITY 0x0800
+#define SCHEDULED_PRIORITY 0x1000
+#define URGENT_PRIORITY 0x1800
+  const CipWord kPriorityMask = 0x1800;
+  const CipWord kPriority = kPriorityMask & connection_parameters;
+
+  CipForwardOpenConnectionPriority priority =
+    kCipForwardOpenConnectionPriorityLow;
+  switch(kPriority) {
+    case LOW_PRIORITY: priority = kCipForwardOpenConnectionPriorityLow; break;
+    case HIGH_PRIORITY: priority = kCipForwardOpenConnectionPriorityHigh; break;
+    case SCHEDULED_PRIORITY: priority =
+      kCipForwardOpenConnectionPriorityScheduled; break;
+    case URGENT_PRIORITY: priority = kCipForwardOpenConnectionPriorityUrgent;
+      break;
+  }
+  return priority;
+#undef LOW_PRIORITY
+#undef HIGH_PRIORITY
+#undef SCHEDULED_PRIORITY
+#undef URGENT_PRIORITY
+}
+
+CipForwardOpenConnectionPriority
+CipForwardOpenGetOriginatorToTargetConnectionPriority(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionPriority( CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+                                  forward_open_data) );
+
+}
+
+static CipForwardOpenConnectionSizeType GetConnectionSizeType(
+  CipWord connection_parameters) {
+#define CONNECTION_SIZE_TYPE_FIXED 0x0000
+#define CONNECTION_SIZE_TYPE_VARIABLE 0x0200
+  const CipWord kConnectionSizeTypeMask = 0x0200;
+  const CipWord kConnectionSizeType = kConnectionSizeTypeMask &
+                                      connection_parameters;
+
+  CipForwardOpenConnectionSizeType type =
+    kCipForwardOpenConnectionSizeTypeFixed;
+  switch (kConnectionSizeType) {
+    case CONNECTION_SIZE_TYPE_FIXED: type =
+      kCipForwardOpenConnectionSizeTypeFixed; break;
+    case CONNECTION_SIZE_TYPE_VARIABLE: type =
+      kCipForwardOpenConnectionSizeTypeVariable; break;
+  }
+
+  return type;
+
+#undef CONNECTION_SIZE_TYPE_FIXED
+#undef CONNECTION_SIZE_TYPE_VARIABLE
+
+}
+
+CipForwardOpenConnectionSizeType
+CipForwardOpenGetOriginatorToTargetConnectionSizeType(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionSizeType( CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+                                  forward_open_data) );
+}
+
+static uint16_t GetConnectionSize(CipWord connection_parameters) {
+  const CipWord kConnectionSizeMask = 0x01FF;
+  return kConnectionSizeMask & connection_parameters;
+}
+
+uint16_t CipForwardOpenGetOriginatorToTargetConnectionSize(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionSize( CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+                              forward_open_data) );
+}
+
+MicroSeconds CipForwardOpenGetTargetToOriginatorRequestedPacketInterval(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->target_to_originator_requested_packet_interval;
+}
+
+CipWord CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->target_to_originator_network_connection_parameters;
+}
+
+bool CipForwardOpenGetTargetToOriginatorRedundantOwner(
+  CipForwardOpenData *forward_open_data) {
+  return GetRedundantOwner( CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+                              forward_open_data) );
+}
+
+CipForwardOpenConnectionType CipForwardOpenGetTargetToOriginatorConnectionType(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionType( CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+                              forward_open_data) );
+}
+
+CipForwardOpenConnectionPriority
+CipForwardOpenGetTargetToOriginatorConnectionPriority(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionPriority( CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+                                  forward_open_data) );
+}
+
+CipForwardOpenConnectionSizeType
+CipForwardOpenGetTargetToOriginatorConnectionSizeType(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionSizeType( CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+                                  forward_open_data) );
+}
+
+uint16_t CipForwardOpenGetTargetToOriginatorConnectionSize(
+  CipForwardOpenData *forward_open_data) {
+  return GetConnectionSize( CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+                              forward_open_data) );
+}
+
+CipConnectionObjectTransportClassTriggerDirection
+CipForwardOpenGetTransportClassTriggerDirection(
+  CipForwardOpenData *forward_open_data) {
+#define DIRECTION_CLIENT 0x00
+#define DIRECTION_SERVER 0x80
+  const CipByte kDirectionMask = 0x80;
+  const CipByte kDirection = kDirectionMask &
+                             forward_open_data->transport_class_and_trigger;
+
+  CipConnectionObjectTransportClassTriggerDirection direction =
+    kCipConnectionObjectTransportClassTriggerDirectionClient;
+
+  switch(kDirection) {
+    case DIRECTION_CLIENT: direction =
+      kCipConnectionObjectTransportClassTriggerDirectionClient; break;
+    case DIRECTION_SERVER: direction =
+      kCipConnectionObjectTransportClassTriggerDirectionServer; break;
+  }
+
+  return direction;
+#undef DIRECTION_CLIENT
+#undef DIRECTION_SERVER
+}
+
+CipConnectionObjectTransportClassTriggerProductionTrigger
+CipForwardOpenGetTransportClassTriggerProductionTrigger(
+  CipForwardOpenData *forward_open_data) {
+#define PRODUCTION_TRIGGER_CYCLIC 0x00
+#define PRODUCTION_TRIGGER_CHANGE_OF_STATE 0x10
+#define PRODUCTION_TRIGGER_APPLICATION_OBJECT 0x20
+
+  const CipByte kProductionTriggerMask = 0x70;
+  const CipByte kProductionTrigger = kProductionTriggerMask &
+                                     forward_open_data->
+                                     transport_class_and_trigger;
+  CipConnectionObjectTransportClassTriggerProductionTrigger trigger =
+    kCipConnectionObjectTransportClassTriggerProductionTriggerCyclic;
+
+  switch(kProductionTrigger) {
+    case PRODUCTION_TRIGGER_CYCLIC: trigger =
+      kCipConnectionObjectTransportClassTriggerProductionTriggerCyclic; break;
+    case PRODUCTION_TRIGGER_CHANGE_OF_STATE: trigger =
+      kCipConnectionObjectTransportClassTriggerProductionTriggerChangeOfState;
+      break;
+    case PRODUCTION_TRIGGER_APPLICATION_OBJECT: trigger =
+      kCipConnectionObjectTransportClassTriggerProductionTriggerApplicationObject;
+      break;
+  }
+
+  return trigger;
+
+#undef PRODUCTION_TRIGGER_CYCLIC
+#undef PRODUCTION_TRIGGER_CHANGE_OF_STATE
+#undef PRODUCTION_TRIGGER_APPLICATION_OBJECT
+}
+
+CipConnectionObjectTransportClassTriggerClass
+CipForwardOpenGetTransportClassTriggerClass(
+  CipForwardOpenData *forward_open_data) {
+#define TRANSPORT_CLASS_0 0x00
+#define TRANSPORT_CLASS_1 0x01
+#define TRANSPORT_CLASS_2 0x02
+#define TRANSPORT_CLASS_3 0x03
+  const CipByte kTransportClassMask = 0x0F;
+  const CipByte kTransportClass = kTransportClassMask &
+                                  forward_open_data->transport_class_and_trigger;
+
+  CipConnectionObjectTransportClassTriggerClass transport_class =
+    kCipConnectionObjectTransportClassTriggerClass0;
+  switch(kTransportClass) {
+    case TRANSPORT_CLASS_0: transport_class =
+      kCipConnectionObjectTransportClassTriggerClass0; break;
+    case TRANSPORT_CLASS_1: transport_class =
+      kCipConnectionObjectTransportClassTriggerClass1; break;
+    case TRANSPORT_CLASS_2: transport_class =
+      kCipConnectionObjectTransportClassTriggerClass2; break;
+    case TRANSPORT_CLASS_3: transport_class =
+      kCipConnectionObjectTransportClassTriggerClass3; break;
+  }
+
+  return transport_class;
+
+#undef TRANSPORT_CLASS_0
+#undef TRANSPORT_CLASS_1
+#undef TRANSPORT_CLASS_2
+#undef TRANSPORT_CLASS_3
+}
+
+CipUsint CipForwardOpenGetConnectionPathSizeInWords(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->connection_path_size;
+}
+
+const CipOctet *CipForwardOpenGetConnectionPath(
+  CipForwardOpenData *forward_open_data) {
+  return forward_open_data->connection_path;
+}

+ 145 - 0
source/src/cip/connectionmanager/cipforwardopen.h

@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2016, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#ifndef SRC_CIP_CONNECTIONMANAGER_CIPFORWARDOPEN_H_
+#define SRC_CIP_CONNECTIONMANAGER_CIPFORWARDOPEN_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "typedefs.h"
+#include "cipconnectionobject.h"
+
+typedef struct cip_forward_open_data CipForwardOpenData;
+extern const size_t CipForwardOpenDataSize;
+
+typedef enum cip_forward_open_priority_tick_time {
+  kCipForwardOpenPriorityTickTimeNormal,
+  kCipForwardOpenPriorityTickTimeReserved
+} CipForwardOpenPriorityTickTime;
+
+/**
+ * @brief Connection Type constants of the Forward Open service request
+ *   Indicates either a
+ * - Null Request
+ * - Point-to-point connection request (unicast)
+ * - Multicast connection request
+ */
+typedef enum cip_forward_open_connection_type {
+  kCipForwardOpenConnectionTypeNull,
+  kCipForwardOpenConnectionTypePointToPointConnection,
+  kCipForwardOpenConnectionTypeMulticastConnection,
+  kCipForwardOpenConnectionTypeReserved  /**< Reserved and therefore invalid type */
+} CipForwardOpenConnectionType;
+
+typedef enum cip_forward_open_connection_priority {
+  kCipForwardOpenConnectionPriorityLow,
+  kCipForwardOpenConnectionPriorityHigh,
+  kCipForwardOpenConnectionPriorityScheduled,
+  kCipForwardOpenConnectionPriorityUrgent
+} CipForwardOpenConnectionPriority;
+
+typedef enum cip_forward_open_connection_size_type {
+  kCipForwardOpenConnectionSizeTypeFixed,
+  kCipForwardOpenConnectionSizeTypeVariable
+} CipForwardOpenConnectionSizeType;
+
+const CipOctet *CipForwardOpenGetForwardOpenDataFromMessage(
+  CipForwardOpenData *const data, const CipOctet **const message);
+
+CipForwardOpenPriorityTickTime CipForwardOpenGetPriorityTickTime(
+  CipForwardOpenData *forward_open_data);
+
+uint8_t CipForwardOpenGetTickTime(CipForwardOpenData *forward_open_data);
+
+CipUsint CipForwardOpenGetTimeoutTick(CipForwardOpenData *forward_open_data);
+
+MilliSeconds CipForwardOpenGetTimeoutInMilliseconds(
+  CipForwardOpenData *forward_open_data);
+
+CipUdint CipForwardOpenGetOriginatorToTargetNetworkConnectionId(
+  CipForwardOpenData *forward_open_data);
+
+CipUdint CipForwardOpenGetTargetToOriginatorNetworkConnectionId(
+  CipForwardOpenData *forward_open_data);
+
+CipUint CipForwardOpenGetConnectionSerialNumber(
+  CipForwardOpenData *forward_open_data);
+
+CipUint CipForwardOpenGetOriginatorVendorId(
+  CipForwardOpenData *forward_open_data);
+
+CipUdint CipForwardOpenGetOriginatorSerialNumber(
+  CipForwardOpenData *forward_open_data);
+
+uint16_t CipForwardOpenGetTimeoutMultiplier(
+  CipForwardOpenData *forward_open_data);
+
+MicroSeconds CipForwardOpenGetOriginatorToTargetRequestedPacketInterval(
+  CipForwardOpenData *forward_open_data);
+
+CipWord CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+  CipForwardOpenData *forward_open_data);
+
+bool CipForwardOpenGetOriginatorToTargetRedundantOwner(
+  CipForwardOpenData *forward_open_data);
+
+CipForwardOpenConnectionType CipForwardOpenGetOriginatorToTargetConnectionType(
+  CipForwardOpenData *forward_open_data);
+
+CipForwardOpenConnectionPriority
+CipForwardOpenGetOriginatorToTargetConnectionPriority(
+  CipForwardOpenData *forward_open_data);
+
+CipForwardOpenConnectionSizeType
+CipForwardOpenGetOriginatorToTargetConnectionSizeType(
+  CipForwardOpenData *forward_open_data);
+
+uint16_t CipForwardOpenGetOriginatorToTargetConnectionSize(
+  CipForwardOpenData *forward_open_data);
+
+MicroSeconds CipForwardOpenGetTargetToOriginatorRequestedPacketInterval(
+  CipForwardOpenData *forward_open_data);
+
+CipWord CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+  CipForwardOpenData *forward_open_data);
+
+bool CipForwardOpenGetTargetToOriginatorRedundantOwner(
+  CipForwardOpenData *forward_open_data);
+
+CipForwardOpenConnectionType CipForwardOpenGetTargetToOriginatorConnectionType(
+  CipForwardOpenData *forward_open_data);
+
+CipForwardOpenConnectionPriority
+CipForwardOpenGetTargetToOriginatorConnectionPriority(
+  CipForwardOpenData *forward_open_data);
+
+CipForwardOpenConnectionSizeType
+CipForwardOpenGetTargetToOriginatorConnectionSizeType(
+  CipForwardOpenData *forward_open_data);
+
+uint16_t CipForwardOpenGetTargetToOriginatorConnectionSize(
+  CipForwardOpenData *forward_open_data);
+
+CipConnectionObjectTransportClassTriggerDirection
+CipForwardOpenGetTransportClassTriggerDirection(
+  CipForwardOpenData *forward_open_data);
+
+CipConnectionObjectTransportClassTriggerProductionTrigger
+CipForwardOpenGetTransportClassTriggerProductionTrigger(
+  CipForwardOpenData *forward_open_data);
+
+CipConnectionObjectTransportClassTriggerClass
+CipForwardOpenGetTransportClassTriggerClass(
+  CipForwardOpenData *forward_open_data);
+
+CipUsint CipForwardOpenGetConnectionPathSizeInWords(
+  CipForwardOpenData *forward_open_data);
+
+const CipOctet *CipForwardOpenGetConnectionPath(
+  CipForwardOpenData *forward_open_data);
+
+#endif /* SRC_CIP_CONNECTIONMANAGER_CIPFORWARDOPEN_H_ */

+ 64 - 16
source/src/enet_encap/endianconv.c

@@ -1,6 +1,6 @@
 /*******************************************************************************
  * Copyright (c) 2009, Rockwell Automation, Inc.
- * All rights reserved. 
+ * All rights reserved.
  *
  ******************************************************************************/
 
@@ -26,13 +26,27 @@ OpenerEndianess g_opener_platform_endianess = kOpenerEndianessUnknown;
  *   @param buffer pointer where data should be reed.
  *   @return EIP_UINT8 data value
  */
-EipUint8 GetSintFromMessage(const EipUint8 **const  buffer) {
+EipUint8 GetSintFromMessage(const EipUint8 **const buffer) {
   const unsigned char *const buffer_address = (unsigned char *) *buffer;
   EipUint16 data = buffer_address[0];
   *buffer += 1;
   return data;
 }
 
+CipByte GetByteFromMessage(const CipOctet **const buffer_address) {
+  const CipOctet *buffer = *buffer_address;
+  CipByte data = buffer[0];
+  *buffer_address += 1;
+  return data;
+}
+
+CipUsint GetUsintFromMessage(const CipOctet **const buffer_address) {
+  const CipOctet *buffer = *buffer_address;
+  CipUsint data = buffer[0];
+  *buffer_address += 1;
+  return data;
+}
+
 /* little-endian-to-host unsigned 16 bit*/
 
 /**
@@ -47,6 +61,20 @@ EipUint16 GetIntFromMessage(const EipUint8 **const buffer) {
   return data;
 }
 
+CipUint GetUintFromMessage(const CipOctet **const buffer_address) {
+  const CipOctet *buffer = *buffer_address;
+  EipUint16 data = buffer[0] | buffer[1] << 8;
+  *buffer_address += 2;
+  return data;
+}
+
+CipWord GetWordFromMessage(const CipOctet **const buffer_address) {
+  const CipOctet *buffer = *buffer_address;
+  EipUint16 data = buffer[0] | buffer[1] << 8;
+  *buffer_address += 2;
+  return data;
+}
+
 /**
  *   @brief Reads EIP_UINT32 from *buffer and converts little endian to host.
  *   @param buffer pointer where data should be reed.
@@ -59,6 +87,14 @@ EipUint32 GetDintFromMessage(const EipUint8 **const buffer) {
   return data;
 }
 
+CipUdint GetUdintFromMessage(const CipOctet **const buffer_address) {
+  const CipOctet *buffer = *buffer_address;
+  CipUdint data = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] <<
+    24;
+  *buffer_address += 4;
+  return data;
+}
+
 /**
  * @brief converts UINT8 data from host to little endian an writes it to buffer.
  * @param data value to be written
@@ -112,15 +148,21 @@ int AddDintToMessage(const EipUint32 data, EipUint8 **const buffer) {
  */
 EipUint64 GetLintFromMessage(const EipUint8 **const buffer) {
   const EipUint8 *buffer_address = *buffer;
-  EipUint64 data = ((((EipUint64) buffer_address[0]) << 56)
-      & 0xFF00000000000000LL)
-      + ((((EipUint64) buffer_address[1]) << 48) & 0x00FF000000000000LL)
-      + ((((EipUint64) buffer_address[2]) << 40) & 0x0000FF0000000000LL)
-      + ((((EipUint64) buffer_address[3]) << 32) & 0x000000FF00000000LL)
-      + ((((EipUint64) buffer_address[4]) << 24) & 0x00000000FF000000)
-      + ((((EipUint64) buffer_address[5]) << 16) & 0x0000000000FF0000)
-      + ((((EipUint64) buffer_address[6]) << 8) & 0x000000000000FF00)
-      + (((EipUint64) buffer_address[7]) & 0x00000000000000FF);
+  EipUint64 data = ( ( ( (EipUint64) buffer_address[0] ) << 56 )
+                     & 0xFF00000000000000LL )
+                   + ( ( ( (EipUint64) buffer_address[1] ) << 48 ) &
+                       0x00FF000000000000LL )
+                   + ( ( ( (EipUint64) buffer_address[2] ) << 40 ) &
+                       0x0000FF0000000000LL )
+                   + ( ( ( (EipUint64) buffer_address[3] ) << 32 ) &
+                       0x000000FF00000000LL )
+                   + ( ( ( (EipUint64) buffer_address[4] ) << 24 ) &
+                       0x00000000FF000000 )
+                   + ( ( ( (EipUint64) buffer_address[5] ) << 16 ) &
+                       0x0000000000FF0000 )
+                   + ( ( ( (EipUint64) buffer_address[6] ) << 8 ) &
+                       0x000000000000FF00 )
+                   + ( ( (EipUint64) buffer_address[7] ) & 0x00000000000000FF );
   *buffer += 8;
   return data;
 }
@@ -148,8 +190,9 @@ int AddLintToMessage(const EipUint64 data, EipUint8 **const buffer) {
 #endif
 
 
-int EncapsulateIpAddress(EipUint16 port, EipUint32 address,
-                                           EipByte **communication_buffer) {
+int EncapsulateIpAddress(EipUint16 port,
+                         EipUint32 address,
+                         EipByte **communication_buffer) {
   int size = 0;
   if (kOpENerEndianessLittle == g_opener_platform_endianess) {
     size += AddIntToMessage(htons(AF_INET), communication_buffer);
@@ -175,7 +218,8 @@ int EncapsulateIpAddress(EipUint16 port, EipUint32 address,
       *communication_buffer += 4;
       size += 4;
     } else {
-      fprintf(stderr, "No endianess detected! Probably the DetermineEndianess function was not executed!");
+      fprintf(stderr,
+              "No endianess detected! Probably the DetermineEndianess function was not executed!");
       exit (EXIT_FAILURE);
     }
   }
@@ -211,12 +255,16 @@ void MoveMessageNOctets(int amount_of_bytes_moved, CipOctet **message_runner) {
   (*message_runner) += amount_of_bytes_moved;
 }
 
-int FillNextNMessageOctetsWith(CipOctet value, unsigned int amount_of_bytes_written, CipOctet **message) {
+int FillNextNMessageOctetsWith(CipOctet value,
+                               unsigned int amount_of_bytes_written,
+                               CipOctet **message) {
   memset(*message, value, amount_of_bytes_written);
   return amount_of_bytes_written;
 }
 
-int FillNextNMessageOctetsWithValueAndMoveToNextPosition(CipOctet value, unsigned int amount_of_filled_bytes, CipOctet **message) {
+int FillNextNMessageOctetsWithValueAndMoveToNextPosition(CipOctet value,
+                                                         unsigned int amount_of_filled_bytes,
+                                                         CipOctet **message) {
   FillNextNMessageOctetsWith(value, amount_of_filled_bytes, message);
   MoveMessageNOctets(amount_of_filled_bytes, message);
   return amount_of_filled_bytes;

+ 20 - 5
source/src/enet_encap/endianconv.h

@@ -1,6 +1,6 @@
 /*******************************************************************************
  * Copyright (c) 2009, Rockwell Automation, Inc.
- * All rights reserved. 
+ * All rights reserved.
  *
  ******************************************************************************/
 #ifndef OPENER_ENDIANCONV_H_
@@ -25,6 +25,10 @@ typedef enum {
  */
 EipUint8 GetSintFromMessage(const EipUint8 **const buffer);
 
+CipByte GetByteFromMessage(const CipOctet **const buffer_address);
+
+CipUsint GetUsintFromMessage(const CipOctet **const buffer_address);
+
 /** @ingroup ENCAP
  *
  * @brief Get an 16Bit integer from the network buffer, and moves pointer beyond the 16 bit value
@@ -33,6 +37,10 @@ EipUint8 GetSintFromMessage(const EipUint8 **const buffer);
  */
 EipUint16 GetIntFromMessage(const EipUint8 **const buffer);
 
+CipUint GetUintFromMessage(const CipOctet **const buffer_address);
+
+CipWord GetWordFromMessage(const CipOctet **const buffer_address);
+
 /** @ingroup ENCAP
  *
  * @brief Get an 32Bit integer from the network buffer.
@@ -41,6 +49,8 @@ EipUint16 GetIntFromMessage(const EipUint8 **const buffer);
  */
 EipUint32 GetDintFromMessage(const EipUint8 **const buffer);
 
+CipUdint GetUdintFromMessage(const CipOctet **const buffer_address);
+
 /** @ingroup ENCAP
  *
  * @brief converts UINT8 data from host to little endian an writes it to buffer.
@@ -93,8 +103,9 @@ int AddLintToMessage(const EipUint64 pa_unData, EipUint8 **const buffer);
  * @param address IP address of the socket, has to be provided in big-endian
  * @param communcation_buffer The message buffer for sending the message
  */
-int EncapsulateIpAddress(EipUint16 port, EipUint32 address,
-                                           EipByte **communication_buffer);
+int EncapsulateIpAddress(EipUint16 port,
+                         EipUint32 address,
+                         EipByte **communication_buffer);
 
 /** Identify if we are running on a big or little endian system and set
  * variable.
@@ -111,7 +122,11 @@ int GetEndianess(void);
 
 void MoveMessageNOctets(int n, CipOctet **message_runner);
 
-int FillNextNMessageOctetsWith(CipOctet value, unsigned int n, CipOctet **message);
+int FillNextNMessageOctetsWith(CipOctet value,
+                               unsigned int n,
+                               CipOctet **message);
 
-int FillNextNMessageOctetsWithValueAndMoveToNextPosition(CipOctet value, unsigned int n, CipOctet **message);
+int FillNextNMessageOctetsWithValueAndMoveToNextPosition(CipOctet value,
+                                                         unsigned int n,
+                                                         CipOctet **message);
 #endif /* OPENER_ENDIANCONV_H_ */

+ 1 - 0
source/src/typedefs.h

@@ -8,6 +8,7 @@
 
 #include <inttypes.h>
 #include <stddef.h>
+#include <stdbool.h>
 
 /** @file typedefs.h
    Do not use interface types for internal variables, such as "int i;", which is

+ 1 - 0
source/tests/CMakeLists.txt

@@ -25,6 +25,7 @@ target_link_libraries( OpENer_Tests gcov ${CPPUTEST_LIBRARY} ${CPPUTESTEXT_LIBRA
 target_link_libraries( OpENer_Tests UtilsTest Utils ) 
 target_link_libraries( OpENer_Tests EthernetEncapsulationTest ENET_ENCAP )
 target_link_libraries( OpENer_Tests CipTest CIP )
+target_link_libraries( OpENer_Tests CipConnectionManagerTest CIP_CONNECTION_MANAGER )
 
 ########################################
 # Adds test to CTest environment       #

+ 1 - 0
source/tests/OpENerTests.h

@@ -6,3 +6,4 @@ IMPORT_TEST_GROUP(EndianConversion);
 IMPORT_TEST_GROUP(CipEpath);
 IMPORT_TEST_GROUP(CipElectronicKey);
 IMPORT_TEST_GROUP(CipConnectionObject);
+IMPORT_TEST_GROUP(CipForwardOpen);

+ 2 - 0
source/tests/cip/CMakeLists.txt

@@ -1,6 +1,8 @@
 
 opener_common_includes()
 
+add_subdirectory( connectionmanager )
+
 set( CipTestSrc cipepathtest.cpp cipelectronickeytest.cpp cipconnectionobjecttest.cpp )
 
 include_directories( ${SRC_DIR}/cip )

+ 10 - 0
source/tests/cip/connectionmanager/CMakeLists.txt

@@ -0,0 +1,10 @@
+
+opener_common_includes()
+
+set( CipConnectionManagerTestSrc cipforwardopentest.cpp )
+
+include_directories( ${SRC_DIR}/cip ${SRC_DIR}/cip/connectionmanager )
+
+add_library( CipConnectionManagerTest ${CipConnectionManagerTestSrc} )
+
+target_link_libraries( CipConnectionManagerTest gcov ${CPPUTEST_LIBRARY} ${CPPUTESTEXT_LIBRARY} )

+ 101 - 0
source/tests/cip/connectionmanager/cipforwardopentest.cpp

@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2016, Rockwell Automation, Inc.
+ * All rights reserved.
+ *
+ ******************************************************************************/
+
+#include <CppUTest/TestHarness.h>
+#include <stdint.h>
+#include <string.h>
+
+extern "C" {
+
+#include "cipforwardopen.h"
+
+}
+
+TEST_GROUP(CipForwardOpen) {
+
+};
+
+CipOctet message[] =
+{ 0x06, 0x28, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x26, 0x00, 0x18,
+  0x00, 0x78,
+  0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x62, 0x02, 0x00, 0xff, 0x43,
+  0x5a, 0x62,
+  0x02, 0x00, 0xff, 0x43, 0xa3, 0x02, 0x20, 0x02, 0x24, 0x01};
+
+TEST(CipForwardOpen, ParseMessage) {
+  CipOctet *array_address = message;
+  CipOctet **message_runner = &array_address;
+
+  CipForwardOpenData *forward_open_data;
+  forward_open_data = (CipForwardOpenData *)calloc(1, CipForwardOpenDataSize);
+  CipForwardOpenGetForwardOpenDataFromMessage(forward_open_data,
+                                              (const CipOctet **)message_runner);
+  CHECK_EQUAL( kCipForwardOpenPriorityTickTimeNormal, CipForwardOpenGetPriorityTickTime(
+                 forward_open_data) );
+  CHECK_EQUAL( 6, CipForwardOpenGetTickTime(forward_open_data) );
+  CHECK_EQUAL( 40, CipForwardOpenGetTimeoutTick(forward_open_data) );
+  CHECK_EQUAL( 2560,
+               CipForwardOpenGetTimeoutInMilliseconds(forward_open_data) );
+  CHECK_EQUAL( 0x00000000, CipForwardOpenGetOriginatorToTargetNetworkConnectionId(
+                 forward_open_data) );
+  CHECK_EQUAL( 0x00000026, CipForwardOpenGetTargetToOriginatorNetworkConnectionId(
+                 forward_open_data) );
+  CHECK_EQUAL( 0x0026, CipForwardOpenGetConnectionSerialNumber(
+                 forward_open_data) );
+  CHECK_EQUAL( 0x0018, CipForwardOpenGetOriginatorVendorId(forward_open_data) );
+  CHECK_EQUAL( 0x12345678,
+               CipForwardOpenGetOriginatorSerialNumber(forward_open_data) );
+  CHECK_EQUAL( 4, CipForwardOpenGetTimeoutMultiplier(forward_open_data) );
+
+  CHECK_EQUAL( 156250, CipForwardOpenGetOriginatorToTargetRequestedPacketInterval(
+                 forward_open_data) );
+  CHECK_EQUAL( 0x43ff, CipForwardOpenGetOriginatorToTargetNetworkConnectionParameters(
+                 forward_open_data) );
+  CHECK_EQUAL( false,
+               CipForwardOpenGetOriginatorToTargetRedundantOwner(
+                 forward_open_data) );
+  CHECK_EQUAL( kCipForwardOpenConnectionTypePointToPointConnection, CipForwardOpenGetOriginatorToTargetConnectionType(
+                 forward_open_data) );
+  CHECK_EQUAL( kCipForwardOpenConnectionPriorityLow, CipForwardOpenGetOriginatorToTargetConnectionPriority(
+                 forward_open_data) );
+  CHECK_EQUAL( kCipForwardOpenConnectionSizeTypeVariable, CipForwardOpenGetOriginatorToTargetConnectionSizeType(
+                 forward_open_data) );
+  CHECK_EQUAL( 511,
+               CipForwardOpenGetOriginatorToTargetConnectionSize(
+                 forward_open_data) );
+
+  CHECK_EQUAL( 156250, CipForwardOpenGetTargetToOriginatorRequestedPacketInterval(
+                 forward_open_data) );
+  CHECK_EQUAL( 0x43ff, CipForwardOpenGetTargetToOriginatorNetworkConnectionParameters(
+                 forward_open_data) );
+  CHECK_EQUAL( false,
+               CipForwardOpenGetTargetToOriginatorRedundantOwner(
+                 forward_open_data) );
+  CHECK_EQUAL( kCipForwardOpenConnectionTypePointToPointConnection, CipForwardOpenGetTargetToOriginatorConnectionType(
+                 forward_open_data) );
+  CHECK_EQUAL( kCipForwardOpenConnectionPriorityLow, CipForwardOpenGetTargetToOriginatorConnectionPriority(
+                 forward_open_data) );
+  CHECK_EQUAL( kCipForwardOpenConnectionSizeTypeVariable, CipForwardOpenGetTargetToOriginatorConnectionSizeType(
+                 forward_open_data) );
+  CHECK_EQUAL( 511,
+               CipForwardOpenGetTargetToOriginatorConnectionSize(
+                 forward_open_data) );
+
+  CHECK_EQUAL( kCipConnectionObjectTransportClassTriggerDirectionServer, CipForwardOpenGetTransportClassTriggerDirection(
+                 forward_open_data) );
+  CHECK_EQUAL(
+     kCipConnectionObjectTransportClassTriggerProductionTriggerApplicationObject,
+    CipForwardOpenGetTransportClassTriggerProductionTrigger(forward_open_data) );
+  CHECK_EQUAL( kCipConnectionObjectTransportClassTriggerClass3, CipForwardOpenGetTransportClassTriggerClass(
+                 forward_open_data) );
+  CHECK_EQUAL( 2,
+               CipForwardOpenGetConnectionPathSizeInWords(forward_open_data) );
+
+  const CipOctet expected_connection_path[] = {0x20, 0x02, 0x24, 0x01};
+  MEMCMP_EQUAL(expected_connection_path,
+               CipForwardOpenGetConnectionPath(forward_open_data), 4);
+  free(forward_open_data);
+}