Connect to more than 3 connection points

129 views
Skip to first unread message

John Arvanitis

unread,
May 20, 2020, 1:00:45 PM5/20/20
to EIP Stack Group OpENer Developers
I want to have the following connection points


   O       ->   T 
---------------------------------
0x101   ->    0x100
0x100   ->    0x101   and

0x101   ->    0x110
0x110   ->    0x101  and

0x101   ->    0x111
0x111   ->    0x101  and

0x101   ->    0x112
0x112   ->    0x101  and

0x101   ->    0x113
0x113   ->    0x101   


I use the following sample application 

/*******************************************************************************
 * Copyright (c) 2012, Rockwell Automation, Inc.
 * All rights reserved.
 *
 ******************************************************************************/

#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <syslog.h>
#include <unistd.h>  //Header file for sleep(). man 3 sleep for details.
#include <pthread.h>
#include <stdbool.h>
#include <time.h>

#include "opener_api.h"
#include "appcontype.h"
#include "trace.h"
#include "cipidentity.h"
#include "ciptcpipinterface.h"
#include "cipqos.h"
#include "nvdata.h"

#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
#include "cipethernetlink.h"
#include "../../amco_application/ethlinkcbs.h"
#endif

#include "../XlsHandler.h"
#include "../UtilitiesC.h"
#include "../UtilitiesCpp.h"

int newCipParam(char* name,unsigned char *value,int valueSize);
void lg(char verbose,char * text);

pthread_mutex_t lock1;

int DEMO_APP_OUTPUT_ASSEMBLY_NUM_101 =           101; //0x180
int DEMO_APP_INPUT_ASSEMBLY_NUM_100 =            100; //0x188
int DEMO_APP_INPUT_ASSEMBLY_NUM_110 =            110; //0x189
int DEMO_APP_INPUT_ASSEMBLY_NUM_111 =            111; //0x18A
int DEMO_APP_INPUT_ASSEMBLY_NUM_112 =            112; //0x18B
int DEMO_APP_INPUT_ASSEMBLY_NUM_113 =            113; //0x18C
int DEMO_APP_CONFIG_ASSEMBLY_NUM =               1; //0x001
int DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM = 254; //0x098
int DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM =254; //0x099

EipUint8 l_assembly_data101[assembly_data101_size];//={0};//128
EipUint8 l_assembly_data100[assembly_data100_size+4];//724+4
EipUint8 l_assembly_data110[assembly_data110_size+4];
EipUint8 l_assembly_data111[assembly_data111_size+4];
EipUint8 l_assembly_data112[assembly_data112_size+4];
EipUint8 l_assembly_data113[assembly_data113_size+4];

EipUint8 g_assembly_data101[assembly_data101_size];//={0};//128
EipUint8 g_assembly_data100[assembly_data100_size+4];//724+4
EipUint8 g_assembly_data110[assembly_data110_size+4];
EipUint8 g_assembly_data111[assembly_data111_size+4];
EipUint8 g_assembly_data112[assembly_data112_size+4];
EipUint8 g_assembly_data113[assembly_data113_size+4];
EipUint8 g_assembly_data097[assembly_data097_size];//32 iarv


extern ParamStruct paramStructArray[paramStructArraySize];

void updateParamStructArray(EipUint8 l_assembly_data101[],int size);
//void updateParamStructArray(EipUint8 *array,int size,char msgType[5]);
//int call_from_c_newParam(CIP_Manager* p, unsigned char * value, int valueSize);

/* global variables for demo application (4 assembly data fields)  ************/

/* local functions */

/* global functions called by the stack */
EipStatus ApplicationInitialization(void) {
/* create 3 assembly object instances*/
/*INPUT*/
CreateAssemblyObject( DEMO_APP_OUTPUT_ASSEMBLY_NUM_101, g_assembly_data101,
sizeof(g_assembly_data101));

/*OUTPUT*/
CreateAssemblyObject( DEMO_APP_INPUT_ASSEMBLY_NUM_100, g_assembly_data100,
sizeof(g_assembly_data100));

/*OUTPUT*/
CreateAssemblyObject( DEMO_APP_INPUT_ASSEMBLY_NUM_110, g_assembly_data110,
sizeof(g_assembly_data110));

/*OUTPUT*/
CreateAssemblyObject( DEMO_APP_INPUT_ASSEMBLY_NUM_111, g_assembly_data111,
sizeof(g_assembly_data111));

/*OUTPUT*/
CreateAssemblyObject( DEMO_APP_INPUT_ASSEMBLY_NUM_112, g_assembly_data112,
sizeof(g_assembly_data112));
/*OUTPUT*/
CreateAssemblyObject( DEMO_APP_INPUT_ASSEMBLY_NUM_113, g_assembly_data113,
sizeof(g_assembly_data113));

/*CONFIG*/
CreateAssemblyObject( DEMO_APP_CONFIG_ASSEMBLY_NUM, g_assembly_data097,
sizeof(g_assembly_data097));

/*Heart-beat output assembly for Input only connections */
//CreateAssemblyObject(DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM, NULL, 0);
CreateAssemblyObject(DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM, g_assembly_data_input_heartbeat,
sizeof(g_assembly_data_input_heartbeat));

/*Heart-beat output assembly for Listen only connections */
CreateAssemblyObject(DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM, NULL, 0);
//CreateAssemblyObject(DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM, g_assembly_data_input_heartbeat,
// sizeof(g_assembly_data_input_heartbeat));

/* assembly for explicit messaging */
//CreateAssemblyObject( DEMO_APP_EXPLICT_ASSEMBLY_NUM, g_assembly_data09A,
// sizeof(g_assembly_data09A) );

ConfigureExclusiveOwnerConnectionPoint(0,
DEMO_APP_OUTPUT_ASSEMBLY_NUM_101,
DEMO_APP_INPUT_ASSEMBLY_NUM_100,
DEMO_APP_CONFIG_ASSEMBLY_NUM);


ConfigureInputOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_100,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

ConfigureListenOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_100,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

///////////////////////////////////////////////////
ConfigureExclusiveOwnerConnectionPoint(0,
DEMO_APP_OUTPUT_ASSEMBLY_NUM_101,
DEMO_APP_INPUT_ASSEMBLY_NUM_110,
DEMO_APP_CONFIG_ASSEMBLY_NUM);


ConfigureInputOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_110,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

ConfigureListenOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_110,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

///////////////////////////////////////////////////
ConfigureExclusiveOwnerConnectionPoint(0,
DEMO_APP_OUTPUT_ASSEMBLY_NUM_101,
DEMO_APP_INPUT_ASSEMBLY_NUM_111,
DEMO_APP_CONFIG_ASSEMBLY_NUM);


ConfigureInputOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_111,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

ConfigureListenOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_111,
DEMO_APP_CONFIG_ASSEMBLY_NUM);


///////////////////////////////////////////////////
ConfigureExclusiveOwnerConnectionPoint(0,
DEMO_APP_OUTPUT_ASSEMBLY_NUM_101,
DEMO_APP_INPUT_ASSEMBLY_NUM_112,
DEMO_APP_CONFIG_ASSEMBLY_NUM);


ConfigureInputOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_112,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

ConfigureListenOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_112,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

///////////////////////////////////////////////////
ConfigureExclusiveOwnerConnectionPoint(0,
DEMO_APP_OUTPUT_ASSEMBLY_NUM_101,
DEMO_APP_INPUT_ASSEMBLY_NUM_113,
DEMO_APP_CONFIG_ASSEMBLY_NUM);


ConfigureInputOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_113,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

ConfigureListenOnlyConnectionPoint(0,
DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM,
DEMO_APP_INPUT_ASSEMBLY_NUM_113,
DEMO_APP_CONFIG_ASSEMBLY_NUM);

/* For NV data support connect callback functions for each object class with
*  NV data.
*/
InsertGetSetCallback(GetCipClass(kCipQoSClassCode), NvQosSetCallback,
kNvDataFunc);
InsertGetSetCallback(GetCipClass(kCipTcpIpInterfaceClassCode),
NvTcpipSetCallback,
kNvDataFunc);

#if defined(OPENER_ETHLINK_CNTRS_ENABLE) && 0 != OPENER_ETHLINK_CNTRS_ENABLE
/* For the Ethernet Interface & Media Counters connect a PreGetCallback and
*  a PostGetCallback.
* The PreGetCallback is used to fetch the counters from the hardware.
* The PostGetCallback is utilized by the GetAndClear service to clear
*  the hardware counters after the current data have been transmitted.
*/
{
CipClass *p_eth_link_class = GetCipClass(kCipEthernetLinkClassCode);
InsertGetSetCallback(p_eth_link_class,
EthLnkPreGetCallback,
kPreGetFunc);
InsertGetSetCallback(p_eth_link_class,
EthLnkPostGetCallback,
kPostGetFunc);
/* Specify the attributes for which the callback should be executed. */
for (int idx = 0; idx < OPENER_ETHLINK_INSTANCE_CNT; ++idx)
{
CipAttributeStruct *p_eth_link_attr;
CipInstance *p_eth_link_inst =
GetCipInstance(p_eth_link_class, idx+1);
OPENER_ASSERT(p_eth_link_inst);

/* Interface counters attribute */
p_eth_link_attr = GetCipAttribute(p_eth_link_inst, 4);
p_eth_link_attr->attribute_flags |= (kPreGetFunc | kPostGetFunc);
/* Media counters attribute */
p_eth_link_attr = GetCipAttribute(p_eth_link_inst, 5);
p_eth_link_attr->attribute_flags |= (kPreGetFunc | kPostGetFunc);
}
}
#endif

return kEipStatusOk;
}



Unfortunately the connections are failed . The pcap file is attached.

Do you have any idea about the connection failures? Is there arrror in ApplicationInitialization function? 
CIP_OAS_2 (1).pcapng

stm-at-esd

unread,
May 21, 2020, 7:49:43 PM5/21/20
to EIP Stack Group OpENer Developers
Hi,

I had only time to look at the source code so far, but I think your program needs some changes to work with multiple exclusive owner / input only / listen only connections.

a) When you register connection points to the OpENer stack using ConfigureExclusiveOwnerConnectionPoint() the first parameter is the entry index in the stack internal table of exclusive owner connections. You call this function five times but you need to increase the entry index each time, i. e. you need to call

ConfigureExclusiveOwnerConnectionPoint(0, ...);

ConfigureExclusiveOwnerConnectionPoint(1, ...);

ConfigureExclusiveOwnerConnectionPoint(2, ...);

ConfigureExclusiveOwnerConnectionPoint(3, ...);

ConfigureExclusiveOwnerConnectionPoint(4, ...);


b) To have enough slots in the internal table you need to increase OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS from the default 1 to at least 5.

c) This holds true in the same manner for the ConfigureInputOnlyConnectionPoint() and ConfigureListenOnlyConnectionPoint() calls and the constants OPENER_CIP_NUM_INPUT_ONLY_CONNS and OPENER_CIP_NUM_LISTEN_ONLY_CONNS, respectively.

I have not checked the assignment of the Exclusive-Owner / Input Only / Listen Only Connection Point configuration to the individual assemblies. In particular, I believe that a separate configuration assembly must (should?) be created for each Exclusive-Owner Connection.

But I have to think about this again when I've more time for that.

The best way is to describe how your device should look like from the EtherNet/IP point of view. E.g. how many input data blocks there are, are there also outputs, are they independent or dependent on each other. 

Best regards,
    Stefan

Alois Zoitl

unread,
May 22, 2020, 8:17:02 AM5/22/20
to eip-stack-group-...@googlegroups.com
Hi,

it is a long time since I originally implemented that code. And I expect that martin has improved it since then a lot.

If I remember correctly the important point for all these connection entries is always the output assembly. This has to be unique across all entries and the
main distinguishing factor from the CIP spec. I think OpENer still not really makes use of the configuration assembly. Therefore you can for starting share it
among all your connection points.

Cheers,
Alois
> --
> You received this message because you are subscribed to the Google Groups "EIP Stack Group OpENer Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to eip-stack-group-opener...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/eip-stack-group-opener-developers/0317d0bb-07c9-4547-9cc8-f4f367fdf928%40googlegroups.com.

Martin Melik-Merkumians

unread,
May 24, 2020, 7:02:39 AM5/24/20
to EIP Stack Group OpENer Developers
Hi.

your Wireshark trace is rather long and stuffed will all kinds of communication. I have only checked the first two Forward Open and the Extended Status says Invalid Producing Path.

Looking at your code, you are using the same connection point number for both Listen_Only and Input_Only heartbeat assemblies (Comments about the hex numbers is wrong 0x98 = 152, 0x99 = 153).

int DEMO_APP_HEARTBEAT_INPUT_ONLY_ASSEMBLY_NUM = 254; //0x098
int DEMO_APP_HEARTBEAT_LISTEN_ONLY_ASSEMBLY_NUM =254; //0x099

This is a problem, as ENIP uses different assemblies to differentiate if a connection is a listen only or input only connection.

Otherwise, Stefan is correct, you are overwriting the allowed connection point combinations every time, so the only valid combinations are the ones with assembly 113.
As far as I understand the configuration assembly, one or more can be present in a device. E.g. to configure an Input or Output Module for a certain measurement or output range (0-10V, 0-24V, 0-20mA, etc.)
In OpENer the configuration is just an example, and it is up to the device vendor to provide meaningful combinations or to not provide a configuration assembly at all.

Best regards,
Martin

John Arvanitis

unread,
May 25, 2020, 4:30:39 AM5/25/20
to EIP Stack Group OpENer Developers
Thanks a lot
Reply all
Reply to author
Forward
0 new messages