文章连载中免费中,且看且珍惜。完结后将转为VIP文章
本文专业性较强,请先阅读相关文档。由于OVDA文档不公开 目前网上能下载到的版本都比较老:
CIP Common Specification Volume 1 -1.0 .pdf P792
EtherNet/IP Adaptation of CIP Specification Volume 2 -1.0.pdf P122
THE CIP NETWORKS LIBRARY Volume 2 EtherNet/IP Adaptation of CIP Edition 1.4 November 2007 P158
开发CIP、Ethernet/IP。
仔细研究OVDA的CIP、Ethernet/IP协议才了解到。
CIP协议分为7卷,协议本来是不公开的,需要成为会员或者购买。本文内容是笔者购买后分享。
CIP是一种考虑到自动化行业而设计的通用协议。然而,由于其开放性,它可以并且已经应用于更多的领域。CIP网络库包含若干卷:

于是狠下心购买一份。作为开发者,肯定不能离开靠谱和最新的资料,也都是吃知识产权的饭的人。
CIP协议7卷
购买
资料
实例编号对照表

CIP is a very versatile protocol designed with the automation industry in mind. However, due to its open nature, it can be and has been applied to many more areas. The CIP Networks Library contains several volumes:
-
Volume 1 deals with the common aspects of CIP that apply to all of the network adaptations. This volume contains the common object library and the device profile library, along with a general description of the communications model, device configuration and CIP data management. This volume also defines an auxiliary power distribution system that is common to all adaptations of CIP.
-
Volume 2 is the EtherNet/IP Adaptation of CIP, which describes how CIP is adapted to the Ethernet TCP/ IP and UDP/IP transportation layers. It also contains any extensions to the material in Volume 1 that are necessary for EtherNet/IP, such as the optional industrial physical layer and connectors.
-
Volume 3 is the DeviceNet Adaptation of CIP, which describes how CIP is adapted to the CAN data link layer. It also contains any extensions to the material in Volume 1 that are necessary for DeviceNet.
-
Volume 4 is the ControlNet Adaptation of CIP, which describes how CIP is adapted to the ControlNet data link layer. It contains a complete description of the ControlNet data link layer and any extensions to the material in Volume 1 that are necessary for ControlNet.
-
Volume 5 is CIP Safety. It contains the information necessary to implement the CIP Safety protocol on CIP Networks.
-
Volume 6 is the CompoNet Adaptation of CIP, which describes how CIP is adapted to the CompoNet data link layer. It contains a complete description of the CompoNet data link layer and any extensions to the material in Volume 1 that are necessary for CompoNet.
-
Volume 7 is the Integration of Modbus Devices into the CIP Architecture. This volume describes a standard for the integration of Modbus devices into the CIP world.
居于TCP/UDP OSI第二层数据链路功能 的 封装协议
+介绍
+使用TCP和UDP
+封装消息
-封装数据包结构
-命令字段
-长度字段
-会话句柄
-状态字段
-发件人上下文字段
-选项字段
-命令特定数据字段
+命令说明
-NOP
-ListIdentity
-ListInterfaces
-RegisterSession
-UnRegisterSession
-ListServices
-SendRRData
-SendUnitData
+会话管理。
-TCP封装会话的阶段
-建立会议
-终止会话
-维护一个会话
-TCP行为(资料性)
+通用数据包格式
-一般
-地址项目
-数据项目
基于 TCP/IP 的显式和 I/O 消息传递
3-1介绍
3-2CIP数据包通过TCP/IP
-未连接的消息
-CIP传输类0和1类连接
-CIP传输类别0和类别1数据包
-类别0和类别1连接的行为(资料性)
-CIP运输等级2和等级3连接
-CIP运输类别4至6
3-3连接管理器对象
-连接参数
-连接类型
-优先
-触发类型
-连接大小
-连接请求超时
-连接路径
-网络连接ID
-CIP传输类2和类3连接的Forward_open
-CIP传输类0和1类连接的Forward_open
-一般
-将连接映射到IP多播地址
-完成多播连接(资料性)。
3-4 CIP传输类别0和1类连接数据
-UDP数据报
-CIP传输类别0和类别1数据包排序
-筛选传入连接的数据
3-5 IP组播范围和地址分配
-背景(资料性的)
-一般
-当前的范围界定实践
-目前的地址分配实践
-不断发展的互联网标准
-临时范围策略。
-临时分配策略
===
/******************************************************************************* * 版权所有 (c) 2009, Rockwell Automation, Inc. * 版权所有。 * ******************************************************************************/ #ifndef OPENER_OPENER_API_H_ #define OPENER_OPENER_API_H_ #include <assert.h> #include <stdbool.h> #include "typedefs.h" #include "ciptypes.h" #include "ciperror.h" /** @defgroup CIP_API OpENer 用户界面 * @brief 这是 OpENer 的公共接口。它提供所有功能可以实现一个支持 EtherNet/IP 的从设备。 */ /** @ingroup CIP_API * @brief 从 指定硬件接口读取网络配置数据 * * @param iface 指定硬件接口的字符串地址 * @param iface_cfg 接口配置结构体的地址 * @return 成功时返回 kEipStatusOk, * kEipStatusError 出错时设置 @p errn * * 该函数读取填充 iface_cfg 结构所需的所有信息 来自硬件接口的类型为 @ref CipTcpIpInterfaceConfiguration 由 iface 字符串指定。 */ EipStatus IfaceGetConfiguration(const char *iface, CipTcpIpInterfaceConfiguration *iface_cfg); /** @ingroup CIP_API * @brief 读取并返回以太网接口的 MAC 地址 * * @param iface 接口名称的字符串或接口索引 * @param physical_address 网络接口的硬件 MAC 地址 * @return kEipStatusOk: 一切正常 * kEipStatusError: 失败,errno 被设置 */ EipStatus IfaceGetMacAddress(const char *iface,uint8_t *const physical_address); /** @ingroup CIP_API * @brief 等待网络接口获取 IP 地址 * * @param timeout 秒数;最大值:INT_MAX/10,-1:永久等待 * @param do_run 如果此参数变为零,则停止等待 * @return 成功时返回 kEipStatusOk, * 失败时返回 kEipStatusError 并设置 @p errno * * 这个函数等待网络接口获取 IP 地址,但 * 仅 @p 超时秒数(设置为 -1 则永远等待)。 * 通过设置 @p abort_wait 可以中止轮询等待过程 * 从另一个线程中获取非零值。 */ EipStatus IfaceWaitForIp(const char *const iface,int timeout,volatile int *const abort_wait); /** @ingroup CIP_API * @brief 从平台获取主机名 * * @param hostname CipString 目标结构的地址 * * 该函数从平台读取主机名,并通过 hostname 参数返回 * */ void GetHostName(CipString *hostname); /** @ingroup CIP_API * @brief 设置设备身份对象的序列号。 * * @param serial_number 唯一 32 位数字,用于识别设备 */ void SetDeviceSerialNumber(const EipUint32 serial_number); /** @ingroup CIP_API * @brief 设置设备的状态,同时更新扩展设备状态 * * @param status 完整身份对象的状语词内容 * * 该函数设置状态标志和扩展的内部状态 * 身份对象 ext_status 成员中的设备状态字段。 */ void SetDeviceStatus(const CipWord status); /** @ingroup CIP_API * @brief 初始化和设置 CIP 栈 * * @param unique_connection_id 传递给 Connection_Manager_Init() 以形成 每次启动的唯一连接 ID。 */ EipStatus CipStackInit(const EipUint16 unique_connection_id); /** @ingroup CIP_API * @brief 关闭 CIP 协议栈 * * 这将 * - 关闭所有打开的 I/O 连接, * - 关闭所有打开的显式连接,并 * - 释放栈分配的所有内存。 * * 应用程序分配的内存不会被释放。这必须由应用程序完成! */ void ShutdownCipStack(void); /** @ingroup CIP_API * @brief 获取具有给定类代码的 CIP 对象的指针 * * @param class_code 要检索的对象的类代码 * @return CIP 对象的指针 * 如果对象不在栈中,则返回0 */ CipClass *GetCipClass(const CipUdint class_code); /** @ingroup CIP_API * @brief 获取一个实例的指针 * @param cip_object 指向该实例所属对象的指针 * @param instance_number 要检索的实例的编号 * @return 指向 CIP 实例的指针 * 如果实例不在对象中则为 0 */ CipInstance *GetCipInstance(const CipClass *RESTRICT const cip_object,const EipUint32 instance_number); /** @ingroup CIP_API * @brief 获取实例属性指针 * * 由于实例和对象是自相似的,此函数也可以使用 * 获取对象的属性。 * @param cip_instance 指向属性所属实例的指针 * @param attribute_number 要获取的属性编号 * @return 指向属性的指针 *如果实例不在对象中则为0 * CipAttributeStruct *GetCipAttribute(const CipInstance *const cip_instance,const EipUint16 attribute_number); typedef void (*InitializeCipClass)(CipClass *); /**< CIP类初始化的初始化函数 */ /** @ingroup CIP_API * @brief 为新的 CIP 类和属性分配内存 * * 新的 CIP 类将被注册到栈中,以便能够 接收显式消息。 * * @param class_code 新类的类代码 * @param number_of_class_attributes 类属性的个数 * @param highest_class_attribute_number 已实现的类属性集中的最高属性编号 * @param number_of_class_services 类服务的个数 * @param number_of_instance_attributes 实现的实例属性数量 * @param highest_instance_attribute_number 实现的实例属性集中的最高属性编号 * @param number_of_instance_services 实例服务数量 * @param number_of_instances 创建的初始实例数量 * @param name 类名(用于调试类结构) * @param revision 类版本 * @param initializer 非标准实现 * 类属性,函数实现具体功能 * 返回指向新类对象的指针 * 出错时返回0 */ CipClass *CreateCipClass(const CipUdint class_code, const int number_of_class_attributes, const EipUint32 最高类属性编号, const int 类服务数量, const int 实例属性数量, const EipUint32 最高实例属性编号, const int number_of_instance_services, const int number_of_instances, const char *const name, const EipUint16 revision,InitializeCipClass initializer); /** @ingroup CIP_API * @brief 向给定 CIP 类添加多个 CIP 实例 * * 所需数量的实例作为链表附加到类上。 * * 实例按顺序编号——即链中的第一个节点 * 第一个是1,第二个是2,以此类推。 * 你可以随时添加新实例(你不必创建所有 * 同时删除同一类的实例) 删除实例后 * 已创建不支持乱序实例编号 * 支持在创建新实例时耗尽内存导致 * 断言 * * @param cip_object_to_add_instances 应该添加实例的 CIP 对象 * @param number_of_instances 要生成的实例数量。 * @return 新实例的第一个实例的指针 * 出错时返回0 */ CipInstance *AddCipInstances(CipClass *RESTRICT const cip_object_to_add_instances,const int number_of_instances); /** @ingroup CIP_API * @brief 创建给定类的一个实例,并指定实例编号 * * 这个函数可用于创建顺序外的实例编号 * @param cip_class_to_add_instance 实例应创建的类 * @param instance_id 创建的实例的实例 ID * @return 指向创建的实例的指针,如果给定 ID 的实例 * 已存在则返回现有实例,不创建新实例 */ CipInstance *AddCipInstance(CipClass *RESTRICT const cip_class_to_add_instance,const EipUint32 instance_id); /** @ingroup CIP_API * @brief 在 CIP 类的实例中插入一个属性 * * 注意属性存储在实例的数组指针中 * 属性数组在插入一个具有 * 已经定义,之前的属性将被替换 * * @param cip_instance CIP 类实例指针(实例 0) * @param attribute_number 要插入的属性编号。 * @param cip_data_type 要插入的属性类型。 * @param encode_function 编码函数的函数指针 * @param cip_data 属性数据的指针。 * @param cip_flags 指示属性可设置性和可获取性的标志。 */ void InsertAttribute(CipInstance *const instance,const EipUint16 attribute_number,const EipUint8 cip_type,CipAttributeEncodeInMessage encode_function,void *const data,const EipByte cip_flags); /** @ingroup CIP_API * @brief 分配属性位掩码 * * @param target_class 将位掩码插入的类。 * */ void AllocateAttributeMasks(CipClass *target_class); /** @ingroup CIP_API * @brief 计算属性的字节索引 * * @param attribute_number 属性编号。 * */ size_t CalculateIndex(EipUint16 attribute_number); /** @ingroup CIP_API * @brief 在 CIP 对象的实例中插入一个服务 * * 注意:服务存储在类对象中的数组指针里 如果你插入一个已经定义过的服务,服务数组将不可扩展之前定义的服务将被替换 * * @param cip_class_to_add_service 指向 CIP 对象的指针。(也可能是 * 实例# 0) * @param service_code 要插入的服务的服务代码。 * @param service_function 指向代表服务的函数的指针。 * @param service_name 服务名称 */ void InsertService(const CipClass *const cip_class_to_add_service,const EipUint8 service_code,const CipServiceFunction service_function,char *const service_name); /** @ingroup CIP_API * @brief 为 CIP 类插入 Get 或 Set 回调函数 * * @param cip_class 指向目标 CIP 对象的指针 * @param callback_function 要插入的回调函数 * @param callbacks_to_install 选择受影响回调的标志 * * 这个函数将提供的 @p callback_function 插入到选定的 CIP 类@p cip_class 的回调函数条目。 * 回调目标由 @p callbacks_to_install 选择,它可以是 kPreGetFunc、kPostGetFunc、kPreSetFunc、kPostSetFunc 和 kNvDataFunc 的按位或掩码。 * 如果 kPostSetFunc 或 kNvDataFunc 中任一被设置,则相同的函数 指针 CipClass::PostSetCallback 将被调用。 */ void InsertGetSetCallback(CipClass *const cip_class, CipGetSetCallback callback_function, CIPAttributeFlag callbacks_to_install); //TODO: 更新文档 /** @ingroup CIP_API * @brief 根据 CIP 编码将数据生成到消息缓冲区。 * * 该函数可用于自己的服务中,将数据发送回 请求者(例如,getAttributeSingle 用于特殊结构)。 * @param cip_data_type 要编码的 cip 类型 * @param cip_data 指向数据值的指针。 * @param message_router_response 消息路由器响应结构 */ void EncodeCipBool( const CipBool *const data,ENIPMessage *const outgoing_message); void EncodeCipByte( const CipByte *const data,ENIPMessage *const outgoing_message); void EncodeCipWord( const CipWord *const data,ENIPMessage *const outgoing_message); void EncodeCipDword( const CipDword *const data,ENIPMessage *const outgoing_message); void EncodeCipLword( const CipLword *const data,ENIPMessage *const outgoing_message); void EncodeCipUsint( const CipUsint *const data,ENIPMessage *const outgoing_message); void EncodeCipUint( const CipUint *const data,ENIPMessage *const outgoing_message); void EncodeCipUdint( const CipUdint *const data,ENIPMessage *const outgoing_message); void EncodeCipUlint( const CipUlint *const data,ENIPMessage *const outgoing_message); void EncodeCipSint( const CipSint *const data,ENIPMessage *const outgoing_message); void EncodeCipInt( const CipInt *const data,ENIPMessage *const outgoing_message); void EncodeCipDint( const CipDint *const data,ENIPMessage *const outgoing_message); void EncodeCipLint( const CipLint *const data,ENIPMessage *const outgoing_message); void EncodeCipReal( const CipReal *const data,ENIPMessage *const outgoing_message); void EncodeCipLreal( const CipLreal *const data,ENIPMessage *const outgoing_message); void EncodeCipShortString(const CipShortString *const data,ENIPMessage *const outgoing_message); void EncodeCipString( const CipString *const data,ENIPMessage *const outgoing_message); void EncodeCipString2( const CipString2 *const data,ENIPMessage *const outgoing_message); void EncodeCipStringN( const CipStringN *const data,ENIPMessage *const outgoing_message); void EncodeCipStringI( const CipStringI *const data,ENIPMessage *const outgoing_message); void EncodeCipByteArray( const CipByteArray *const data,ENIPMessage *const outgoing_message); void EncodeCipEPath( const CipEpath *const data,ENIPMessage *const outgoing_message); void EncodeCipEthernetLinkPhyisicalAddress(const void *const data,ENIPMessage *const outgoing_message); /** @ingroup CIP_API * @brief 从消息中根据 CIP 编码检索给定数据 * 缓冲区。 * * 这个函数可以用于自己的服务中,用于处理来自 * 请求者(例如,setAttributeSingle) * @param cip_data_type 要解码的 CIP 类型 * @param cip_data 指向要写入的数据值的指针。 * @param cip_message 指向数据应从中获取的内存的指针 * @return 获取的字节数长度 * -1 .. 错误 */ int DecodeData(const EipUint8 cip_data_type,void *const cip_data,const EipUint8 **const cip_message); /** @ingroup CIP_API * @brief 创建一个装配对象实例 * * @param instance_number 要创建的装配对象实例编号 * @param data 指向装配对象应包含的数据的指针 * @param data_length 装配对象数据的长度 * 返回创建的装配对象的实例指针。出错时返回 NULL * * 配置数据装配对象: * * CIP 栈将配置装配对象与其他任何装配对象相同对待 * 装配对象 * 为了支持配置组装对象,它必须使用 * 这个函数创建。 * 接收到的配置数据的通知由 * AfterAssemblyDataReceived 处理。 */ CipInstance *CreateAssemblyObject(const EipUint32 instance_number,EipByte *const data,const EipUint16 data_length); typedef struct cip_connection_object CipConnectionObject; /** @ingroup CIP_API * @brief 处理连接打开的函数原型 * * @param connection_object 正在打开的连接对象 * 连接 * @param extended_error_code 连接对象的返回错误代码 * * @return CIP 错误码 */ typedef EipStatus (*OpenConnectionFunction)(CipConnectionObject *RESTRICT const connection_object,EipUint16 *const extended_error_code); /** @ingroup CIP_API * @brief 处理连接关闭的函数原型 * * @param connection_object 正在关闭的连接对象 * 连接 */ typedef void (*ConnectionCloseFunction)(CipConnectionObject *connection_object); /** @ingroup CIP_API * @brief 处理连接超时的函数原型 * * @param connection_object 连接超时的连接对象 */ typedef void (*ConnectionTimeoutFunction)(CipConnectionObject *connection_object); /** @ingroup CIP_API * @brief 通过连接发送数据的函数原型 * * @param connection_object 连接超时的连接对象 * * @return EIP 堆栈状态 */ typedef EipStatus (*ConnectionSendDataFunction)(CipConnectionObject *连接对象); /** @ingroup CIP_API * @brief 通过连接接收数据的函数原型 * * @param connection_object 连接超时的连接对象 * @param data CIP 消息的有效载荷 * @param data_length 有效载荷的长度 * * @return 堆栈状态 */ typedef CipError (*ConnectionReceiveDataFunction)(CipConnectionObject *connection_object,const EipUint8 *data,const EipUint16 data_length); /** @ingroup CIP_API * @brief 注册特定对象的打开函数。 * * 使用此函数,任何对象都可以被设置为转发目标。 * 打开/关闭请求。 * @param class_code 类代码 * @param open_connection_function 处理打开过程的函数指针 * * 成功时返回 EIP_OK */ EipStatus AddConnectableObject(const CipUdint class_code, OpenConnectionFunction open_connection_function); /** @ingroup CIP_API * @brief 配置专有所有者连接的连接点。 * * @param connection_number 独家所有者连接的编号。 * 枚举从0开始。必须小于 * OPENER_CIP_NUM_EXLUSIVE_OWNER_CONNS. * @param output_assembly_id 要用于此的 O 到 T 点的 ID * 连接 * @param input_assembly_id 要用于此处的 T 到 O 点的 ID * 连接 * @param configuration_assembly_id 要使用的配置点的 ID * 这个连接 */ void ConfigureExclusiveOwnerConnectionPoint(const unsigned int 连接数量,const unsigned int 输出组件 ID,const unsigned int 输入组件 ID,const unsigned int 配置组件 ID); /** @ingroup CIP_API * @brief 配置仅输入连接的连接点。 * * @param connection_number 仅输入连接的编号。The * 枚举从0开始。必须小于 * OPENER_CIP_NUM_INPUT_ONLY_CONNS. * @param output_assembly_id 用于此连接的 O 到 T 点的 ID * 连接 * @param input_assembly_id 要用于此处的 T 到 O 点的 ID * 连接 * @param configuration_assembly_id 要使用的配置点的 ID * 这个连接 */ void ConfigureInputOnlyConnectionPoint(const unsigned int connection_number,const unsigned int output_assembly_id,const unsigned int input_assembly_id,const unsigned int configuration_assembly_id); /** \ingroup CIP_API * \brief 配置仅监听连接的连接点。 * * @param connection_number 输入连接的数量。 * 枚举从0开始。必须小于 * OPENER_CIP_NUM_LISTEN_ONLY_CONNS. * @param output_assembly_id 要用于此的 O 到 T 点的 ID * 连接 * @param input_assembly_id 要用于此处的 T 到 O 点的 ID * 连接 * @param configuration_assembly_id 要使用的配置点的 ID * 这个连接 */ void ConfigureListenOnlyConnectionPoint(const unsigned int connection_number,const unsigned int output_assembly_id,const unsigned int input_assembly_id,const unsigned int configuration_assembly_id); /** @ingroup CIP_API * @brief 通知封装层已发送显式消息 * 通过 TCP 接收。 * * @param socket_handle 接收数据的套接字句柄。 * @param buffer 包含接收数据的缓冲区。如果需要发送响应,此缓冲区也将 * 包含响应。 * @param length 缓冲区中数据的长度。 * @param number_of_remaining_bytes 返回输入中剩余多少字节 * 完成这里之后就结束了 * @param originator_address 消息发送者的地址结构体 * @param outgoing_message The outgoing ENIP message * @return kEipStatusOkSend: a response needs to be sent, others: EIP stack status */ EipStatus HandleReceivedExplictTcpData(int socket_handle,EipUint8 *buffer,size_t length,int *number_of_remaining_bytes,struct sockaddr *originator_address,ENIPMessage *const outgoing_message); /** @ingroup CIP_API * @brief 通知封装层已发送显式消息 * 通过 UDP 接收。 * * @param socket_handle 接收数据的 socket 句柄。 * @param from_address 接收数据的远程地址。 * @param buffer 包含接收数据的缓冲区。如果需要发送响应,此缓冲区也将 * 包含响应。 * @param buffer_length 缓冲区中数据的长度。 * @param number_of_remaining_bytes 返回输入中剩余多少字节 * 这里完成后就结束 * @param unicast 数据是否作为单播消息接收? * @param outgoing_message 外发的 ENIP 消息 * @return kEipStatusOkSend: 需要发送响应,其他:EIP 堆栈状态 */ EipStatus HandleReceivedExplictUdpData(const int socket_handle,const struct sockaddr_in *from_address,const EipUint8 *buffer,const size_t 缓冲区长度,int *剩余字节数量,bool 单播,ENIPMessage *const 发送消息); /** @ingroup CIP_API * @brief 通知连接管理器,某个连接的数据已经 * 收到。 * * 该函数应由网络层调用。 * @param received_data 指向已接收数据缓冲区的指针 * @param received_data_length 数据缓冲区中的字节数 * @param from_address 接收数据的地址。仅 * 来自连接发起者的数据可能会被接受。避免 * 连接劫持 * @return EIP_OK on success */ EipStatus HandleReceivedConnectedData(const EipUint8 *const received_data,int received_data_length,struct sockaddr_in *from_address); /** @ingroup CIP_API * @brief 检查是否任何连接计时器(传输触发器或 * WatchdogTimeout)已经超时。 * * 如果发生超时,该函数执行必要的操作。 * 函数应每隔 @ref kOpenerTimerTickInMilliSeconds 调用一次 * 毫秒。为了简化算法,如果经过的时间更长,则经过的 * 自上次调用函数以来的时间作为参数给出。 * * @param elapsed_time 自上次调用 ManageConnections 以来的毫秒数 * * @return 成功时返回 EIP_OK */ EipStatus ManageConnections(MilliSeconds elapsed_time); /** @ingroup CIP_API * @brief 触发应用触发连接的生产。 * * 这将在下一个时刻发出指定连接的生产。 * 可能的情况。根据 RPI 的值和生产的 * 禁用计时器。应用程序将在生产发生时通过回调函数得到通知。这个函数只能从 void HandleApplication(void) 中调用。 * EIP_BOOL8 BeforeAssemblyDataSend(S_CIP_Instance *pa_pstInstance) * 回调函数在生产将发生时通知应用程序。这个函数应该只从 void HandleApplication(void) 调用。 * 只能从 void HandleApplication(void) 中调用。 * * 只有当应用程序建立时,连接才能被触发 * 是应用程序触发类型。 * * @param output_assembly_id 输出组件的连接点 * 连接 * @param input_assembly_id 输入组件的连接点 * 连接 * @return 成功时返回 EIP_OK */ EipStatus TriggerConnections(unsigned int output_assembly_id, unsigned int input_assembly_id); /** @ingroup CIP_API * @brief 告知封装层远程主机已关闭 * connection. * * 根据规范清理并关闭会话 * 在封装层中。 * @param socket_handle 已关闭连接的套接字句柄 */ void CloseSession(int socket_handle); /** @defgroup CIP_CALLBACK_API OpENer 要求的回调函数 * @ingroup CIP_API * * @brief 这些函数必须实现,以便为 OpENer 提供 * 方法用于通知应用程序某些状态变化。 */ /** @ingroup CIP_CALLBACK_API * @brief 应用程序初始化的回调函数 * * 该函数将在 CIP 栈完成其 * 初始化。在这个函数中,用户可以设置所有的 CIP 对象。 * 喜欢拥有。 * * 这个功能是为了方便而提供的。在 void * CipStackInit(void) * 函数完成后,也可以生成你的 CIP 对象。 * 返回状态 EIP_ERROR .. 错误 * EIP_OK ... 成功完成 */ EipStatus ApplicationInitialization(void); /** @ingroup CIP_CALLBACK_API * @brief 允许特定设备的应用程序执行其操作 * * 这个函数将在每次栈开始执行时被调用 * 执行 EIP_STATUS ManageConnections(void)。它允许实现 * 设备特定应用功能。在此函数内执行应 * 简洁。 */ void HandleApplication(void); /** @ingroup CIP_CALLBACK_API * @brief 通知应用程序连接发生了变化 * * @param output_assembly_id 输出组件的连接点 * 连接 * @param input_assembly_id 输入组件的连接点 * 连接 * @param io_connection_event 发生变更的信息 */ void CheckIoConnectionEvent(unsigned int output_assembly_id,unsigned int input_assembly_id,IoConnectionEvent io_connection_event); /** @ingroup CIP_CALLBACK_API * @brief 通知应用程序已接收数据的回调函数 * 汇编对象。 * * 此函数必须由 CIP-栈的用户实现。 * @param instance 指向接收数据时的组件对象的数据的指针 * @return 如果数据能够被处理则返回信息 * - EIP_OK 接收到的数据是正确 * - EIP_ERROR 接收到的数据是错误的(尤其是需要) * 配置数据组装对象) * * 配置数据的组装对象: * CIP 栈使用此函数来通知接收到的配置数据。 * 数据长度已在栈内检查。因此 * 用户只需检查数据是否有效。 */ EipStatus 组装后数据接收(Cip 实例 *实例); /** @ingroup CIP_CALLBACK_API * @brief 通知应用程序某个组件的数据 * 对象将被发送。 * * 在此函数中,用户可以更新装配对象的数据 * 在它被发送之前。应用程序可以通知应用程序数据是否 * 已更改。 * @param instance 要发送数据的装配对象实例。 * @return 数据已更改: * - true 装配数据已更改 * - false 装配数据未更改 */ EipBool8 组装数据发送前(CipInstance *instance); /** @ingroup CIP_CALLBACK_API * @brief 尽可能模拟设备的上电重启 * * @return 如果服务被支持,该函数不会返回。 * 如果此服务不受支持,则返回 EIP_ERROR */ EipStatus ResetDevice(void); /** @ingroup CIP_CALLBACK_API * @brief 将设备恢复到初始配置并尽可能模拟 * 可能需要重启设备 * * @return 如果服务受支持,函数将不会返回。 * 如果此服务不受支持,则返回 EIP_ERROR */ EipStatus ResetDeviceToInitialConfiguration(void); /** @ingroup CIP_CALLBACK_API * @brief 为 CIP 栈分配内存 * * 模拟常见的 c 库函数 calloc * 在 OpENer 分配仅发生在应用程序启动时和 * 类/实例创建和配置不在操作期间进行 * (处理消息)。 * @param number_of_elements 要分配的元素数量 * @param size_of_element 每个元素的字节大小 * @return 分配内存的指针,出错时为 0 */ void *CipCalloc(size_t number_of_elements,size_t size_of_element); /** @ingroup CIP_CALLBACK_API * @brief 释放由 OpENer 分配的内存 * * 模拟常见的 c 库函数 free * @param data 指向已分配内存的指针 */ void CipFree(void *data); /** @ingroup CIP_CALLBACK_API * @brief 通知应用程序运行/空闲状态已更改 * 由原创者创建。 * * @param run_idle_value 根据 CIP,run/idle 标志的当前值 * spec 卷 1 3-6.5 */ void RunIdleChanged(EipUint32 run_idle_value); /** @ingroup CIP_CALLBACK_API * @brief 创建生产或消费 UDP 套接字 * * @param communication_direction kUdpCommunicationDirectionProducing 或 kUdpCommunicationDirectionConsuming * @param socket_data 持有地址结构的指针 * 注意:为了建立点对点连接, * *pa_pstAddr->sin_addr.s_addr 成员被 OpENer 设置为 0。网络 * 应用层必须设置正确的地址 * 发起者。 * 注意:为了消耗连接,网络层必须设置 * 将 pa_pstAddr->sin_addr.s_addr 设置为发起者的正确地址。 * FIXME 添加一个参数,该参数可以被 CIP 栈使用来 * 请求发起者的 sockaddr_in 数据 * @param qos_for_socket CIP QoS 对象参数值 * @return 成功时返回 socket 标识符 * 错误时返回-1 */ int CreateUdpSocket(UdpCommuncationDirection communication_direction,struct sockaddr_in *socket_data,CipUsint qos_for_socket); /** @ingroup CIP_CALLBACK_API* @brief 创建生产或消费 UDP 套接字 * * @param socket_data 指向"发送到"地址的指针 * @param socket_handle 发送使用的套接字描述符 * @param outgoing message 构建的出站消息 * @return 成功时返回 kEipStatusOk */ EipStatus SendUdpData(const struct sockaddr_in *const socket_data,const int socket_handle,const ENIPMessage *const outgoing_message); /** @ingroup CIP_CALLBACK_API * @brief 关闭指定的套接字并清理堆栈 * * @param socket_handle 要关闭的套接字描述符 */ void CloseSocket(const int socket_handle); /** @mainpage OpENer - 开源 EtherNet/IP(TM) 通信堆栈 * 文档 * * 适配器设备的 EtherNet/IP 协议栈(连接目标);支持多个 * I/O 和显式连接;包括所需的特性和对象 * CIP 规范,以使设备能够符合 ODVA 的兼容性/ * 互操作性测试。 * * @section intro_sec 简介 * * 这是简介。 * * @section install_sec 构建 * 如何在特定平台上编译、安装和运行 OpENer。 * * @subsection build_req_sec 需求 * OpENer 已开发为高度可移植。默认版本针对 * 使用 POSIX 操作系统和 BSD-socket 网络接口的个人电脑。 * 测试此版本,我们推荐使用 Linux PC 或安装了 Cygwin 的 Windows。 * 您需要安装以下内容: * - gcc, make, binutils 等。 * * 用于正常构建。这些通常应该安装在大多数 Linux 系统上。 * 并且是 Cygwin 开发包的一部分。 * * 对于开发本身,我们推荐使用带有 CDT 的 Eclipse * 插件。为了方便起见,OpENer 已经附带了一个 Eclipse 项目 * 文件。这允许将 OpENer 源树直接导入 Eclipse。 * * @subsection compile_pcs_sec 为 PC 编译 * -# 直接在 shell 中 * -# 进入 bin/pc 目录 * -# 调用 make * -# 为调用 opener 类型:\n * ./opener ipaddress subnetmask gateway domainname hostaddress * macaddress\n * 例如,./opener 192.168.0.2 255.255.255.0 192.168.0.1 test.com * testdevice 00 15 C5 BF D0 87 * -# 在 Eclipse 中 * -# 导入项目 * -# 在“make targets”视图中前往 bin/pc 文件夹 * -# 从构建目标中选择全部 * -# 生成的可执行文件将在目录中 * ./bin/pc * -# 命令行参数可以在运行配置中设置 * Eclipse 对话框 * * @section further_reading_sec 更多主题 * - @ref 移植 * - @ref 扩展 * - @ref license * * @page porting Porting OpENer * @section gen_config_section General Stack Configuration * 在构建你的之前,必须定义一般堆栈属性 * 生产环境。这是通过提供一个名为 opener_user_conf.h 的文件来完成的。 * 示例文件可以在 src/ports/POSIX 或 src/ports/WIN32 目录中找到。 * 示例文件的文档说明了必要的配置选项: * opener_user_conf.h * * @copydoc ./ports/POSIX/sample_application/opener_user_conf.h * * @section startup_sec 启动序列 * 在您的 EtherNet/IP(TM)设备启动期间,必须执行以下步骤: * 执行 * -# 配置网络属性 * 通过以下功能,OpENer 的网络接口是 * 配置: * - EIP_STATUS ConfigureNetworkInterface(const char *ip_address, * const char *subnet_mask, const char *gateway_address) * - void ConfigureMACAddress(const EIP_UINT8 *mac_address) * - void ConfigureDomainName(const char *domain_name) * - void ConfigureHostName(const char *host_name) * . * 根据您的平台,这些数据可能来自配置 * 文件或从操作系统函数。如果这些值应该 * 可通过明确消息远程设置 SetAttributeSingle 函 * EtherNetLink 和 TCPIPInterface 对象的配置需要调整。 * -# 设置设备的序列号\n * 根据 CIP 规范,设备供应商必须确保 * 其每个设备都有一个唯一的 32 位设备 ID。您可以使用 * 该函数设置: * - void setDeviceSerialNumber(EIP_UINT32 serial_number) * -# 初始化 OpENer: \n * With the function CipStackInit(EIP_UINT16 unique_connection_id) the * internal data structures of opener are correctly setup. After this * 步骤中可以创建自己的 CIP 对象和装配对象实例。对于 * 为了您的便利,我们提供回调函数 * 应用程序初始化。当调用这个回调函数时 * 栈已准备好接收特定于应用程序的 CIP 对象。 * -# 创建特定应用程序的 CIP 对象:\n * 在回调函数 ApplicationInitialization(void)中或 * 在 CipStackInit(void)完成后,您可以创建和配置任何 * CIP 对象或装配对象实例。参见模块 @ref CIP_API * 用于可用函数。目前没有可用函数可以 * 删除所有已创建的对象或实例。这是计划中的功能 * 用于未来版本。 * -# 设置监听的 TCP 和 UDP 端口:\n * 以太网/IP 规范要求设备监听 TCP * 在 AF12hex 端口上进行连接和 UDP 数据报文,用于显式消息。 * 因此在进入正常运行之前,您需要配置您的 * 网络库,以便在此端口上的 TCP 和 UDP 消息将 * 已接收并可交由以太网封装层处理。 * * @section normal_op_sec 正常操作 * 在正常操作期间,平台必须完成以下任务 * 特定代码: * - 在 TCP 端口 AF12hex 上建立请求的连接 * - 在已连接的 TCP 套接字和 UDP 套接字上接收显式消息数据 * 用于端口 AF12hex。收到的数据必须交给以太网 * 封装层,具有以下功能:\n * int HandleReceivedExplictTCPData(int socket_handle, EIP_UINT8* buffer, int * buffer_length, int *number_of_remaining_bytes),\n * int HandleReceivedExplictUDPData(int socket_handle, struct sockaddr_in * *from_address, EIP_UINT8* buffer, unsigned int buffer_length, int * *剩余字节数)\n * 根据数据是否是从 TCP 套接字或 UDP 套接字接收的。 * 由于此函数的结果,可能需要发送响应。数据要 * 发送内容在给定的缓冲区 pa_buf 中。 * - 创建用于隐式连接的 UDP 发送和接收套接字 * 消息\n * OpENer 将使用回调函数 int CreateUdpSocket( * UdpCommuncationDirection connection_direction, * struct sockaddr_in *pa_pstAddr) * for informing the platform specific code that a new connection is * established and new sockets are necessary * - Receive implicit connected data on a receiving UDP socket\n * 收到的数据必须交给连接管理对象 * 通过函数 EIP_STATUS HandleReceivedConnectedData(EIP_UINT8 * *data, int data_length) * - 关闭 UDP 和 TCP 套接字: * -# 由 OpENer 通过回调函数请求 * CloseSocket(int socket_handle) * -# 对于 TCP 连接,当对端关闭连接时 OpENer 需要 * 被通知以清理内部数据结构。这是通过 * 使用 * 函数 void CloseSession(int socket_handle)。 * . * - 循环更新连接状态:\n * 以便 OpENer 能够确定何时生成新数据 * 连接或连接超时每 @ref kOpenerTimerTickInMilliSeconds 毫秒 * 毫秒 * 函数 EIP_STATUS ManageConnections(void) 必须被调用。 * * @section callback_funcs_sec 回调函数 * 为了使 OpENer 更加跨平台,并通知 * 应用程序在某些状态变化和堆栈中的操作时,会触发一组 * 提供了回调函数。这些回调函数是在声明。 * 文件 opener_api.h 以及应用特定的实现 * 代码。有关 OpENer 回调 API 的概述和说明,请参阅 * 模块 @ref CIP_CALLBACK_API。 * * @page 扩展 扩展 OpENer * OpENer 提供了一个 API,用于添加自己的 CIP 对象和实例。 * 具体的服务和属性。因此 OpENer 可以轻松适应到 * 支持不同的设备配置文件以及您所需的特定 CIP 对象 * 设备。要使用的功能是: * - CipClass *CreateCipClass( const CipUdint class_code, const int 类属性数量, const EipUint32 最高类属性编号, const int 类服务数量, const int 实例属性数量, const EipUint32 最高实例属性编号, const int 实例服务数量, const int 实例数量, char *名称, const EipUint16 revision, InitializeCipClass initializer ); * - CipInstance *AddCipInstances(CipClass *RESTRICT const cip_class, const int number_of_instances) * - CipInstance *AddCipInstance(CipClass *RESTRICT const class const EipUint32 instance_id) * - void InsertAttribute(CipInstance *const cip_instance, const EipUint16 attribute_number, const EipUint8 cip_data_type, void *const cip_data, const EipByte cip_flags); * - void InsertService(const CipClass *const cip_class_to_add_service, const EipUint8 service_code, const CipServiceFunction service_function, char *const service_name); * * @page license OpENer Open Source License * OpENer 开源许可证是一种改编自 BSD 风格的许可证。其 * 改编包括使用 EtherNet/IP(TM) 这一术语以及使用 OpENer 在 * 自己产品中的必要保护条件。请查看下方许可证文本以获取 * 详细信息: * * @include "../license.txt" * */ #endif /*OPENER_OPENER_API_H_*/
开发者流程
随着 ODVA 员工和会员的协作,开发人员中心不断发展,提供您所需的工具和培训,以增长您的知识并更快地将您的产品推向市场。
以下是 ODVA 工具和培训的简短列表,可帮助您入门。
- 培训课程:面向 EtherNet/IP 供应商的 ODVA 快速入门
- 时间:计划在做出最终实施决策(例如,制造与购买)或确定硬件和软件方法之前参加培训。
- 目的:本课程由 ODVA 成员的工程师授课,他们是 EtherNet/IP 技术和实施方面的专家,将帮助您了解 EtherNet/IP 开发过程、不同的实施方法以及如何避免潜在的陷阱。
- 受众:面向开发工程师和产品经理。
- 查找:在“活动”下注册或请求有关未来快速入门的信息。
- 数字学习工具:EtherNet/IP 开发人员工具箱
- 时间:在您的产品中实施 EtherNet/IP 之前;学习 EtherNet/IP 规范的关键组件时。
- 目的:集成学习环境通过 EtherNet/IP 短期课程、对 EtherNet/IP 规范的交叉引用以及虚拟实验室帮助新开发人员更快地上手,您可以在其中开发基本的虚拟 EtherNet/IP 设备。然后,您可以使用适用于EtherNet/IP 的 ODVA 一致性测试软件来模拟一致性测试体验,以评估您的虚拟设备。
- 受众:刚接触 EtherNet/IP 的开发人员。
- 获取:订购 EtherNet/IP ToolBox。
- 推荐阅读:EtherNet/IP 设备的 ODVA 推荐功能(第 70 版)
- 时间:在最终确定产品规格之前阅读。
- 目的:本出版物包含对重要产品功能的建议,这些功能可以提高产品的互作性和可靠性,并基于 ODVA 成员多年的经验。
- 受众:面向开发工程师、产品经理和技术经理。
- 在文档库中查找它。
- 推荐阅读:参见 ODVA 规范强制变更清单 (Pub 317 R21)。
- 时间:在最终确定产品规格之前阅读
- 目的:本文档跟踪对实施这些技术的产品强制性的规范所做的更改。ODVA 将此列表与规范分开发布,以便向开发人员和管理人员提前通知将影响他们负责的产品的更改。本文档未列出任何可选更改。有关可选的更改和增强功能,请参阅每个规范卷前面的修订版页面。
- 受众:面向开发工程师、产品经理和技术经理。
- 在文档库中查找它。
- MIB:LLDP 的管理信息库 (MIB)
- 目的:LLDP MIB 内容允许使用通用方法实现 ODVA LLDP 信息(除了 LLDP 数据表对象)
- 受众:开发工程师。
- 获取:ODVA 将 LLDP MIB 与 2022 年 11 月及更高版本的 EtherNet/IP 规范一起分发。您也可以注册下载。
- 软件:ODVA 的 EZ-EDS
- 时间:在写入 EDS 文件之前。
- 用途:推荐编辑器为任何 EtherNet/IP、DeviceNet、ControlNet 和/或 CompoNet 产品编写电子数据表 (EDS) 文件,并检查文件中的语法是否正确。
- 受众:开发工程师。
- 获取:注册下载。
- 软件:ODVACompress
- 时间:在分发 EDS 文件之前。
- 用途:从 EDS 文件及其关联的图标文件创建压缩文件。
- 受众:开发工程师。
- 获取:注册以下载 ODVACompress™ Utility 软件和源代码。
- 软件订阅(必填):ODVA 一致性测试软件
- 时间:ODVA 要求在您提交产品进行一致性测试之前进行合规性测试。
- 目的:ODVA 用于验证您的产品是否符合规范主要部分的相同软件,一致性测试软件允许您在将产品提交给 ODVA 进行一致性测试之前在开发过程中发现问题。ODVA 为每个网络提供单独的测试软件包。
- 受众:开发工程师。
- 查找:订购您的订阅(费用在表格上)。
- 实验室:用于 EtherNet/IP 的 ODVA PlugFest
- 时间:当您有一个有效的 EtherNet/IP 实施时,但在产品发布之前。
- 目的:该实验室由 ODVA 成员的工程师作,他们是 EtherNet/IP 实施和互作性问题方面的专家,可让您将产品与其他供应商和一群同行开发人员的产品一起放入 EtherNet/IP 系统中。了解您的产品与其他 EtherNet/IP 设备的互作情况。
- 受众:开发工程师。
- 找到它:在“活动”下注册或索取有关未来 PlugFest 的信息。
https://gitee.com/powes/,作者:前沿风暴,转载请注明原文链接:https://www.cnblogs.com/Kreos/p/19033217