├── ab_cip_test ├── ab_plc_cip_net ├── makefile ├── socket.h ├── ab_cip_private.h ├── ab_cip_helper.h ├── ab_plc_cip_net.sln ├── typedef.h ├── utill.h ├── ab_cip.h ├── socket.c ├── main.c ├── utill.c ├── ab_plc_cip_net.vcxproj ├── ab_cip_helper.c └── ab_cip.c ├── config.mk ├── makefile ├── LICENSE ├── .gitattributes ├── common.mk ├── .gitignore ├── README.md └── README_EN.md /ab_cip_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-pms/ab_plc_cip_net/HEAD/ab_cip_test -------------------------------------------------------------------------------- /ab_plc_cip_net/makefile: -------------------------------------------------------------------------------- 1 | 2 | #只生成 .d,.o即可 3 | BIN = ab_cip_test #这个可以保证生成nginx可执行文件 4 | include $(BUILD_ROOT)/common.mk 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | export BUILD_ROOT = $(shell pwd) 2 | 3 | export INCLUDE_PATH = $(BUILD_ROOT)/include 4 | 5 | BUILD_DIR = $(BUILD_ROOT)/ab_plc_cip_net/ 6 | 7 | export DEBUG = true 8 | 9 | export BUILD_SO = false 10 | 11 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | include config.mk 3 | all: 4 | #-C是指定目录 5 | #make -C signal 6 | 7 | #可执行文件应该放最后 8 | #make -C app 9 | 10 | #用shell命令for搞,shell里边的变量用两个$ 11 | @for dir in $(BUILD_DIR); \ 12 | do \ 13 | make -C $$dir; \ 14 | done 15 | 16 | 17 | clean: 18 | #-rf:删除文件夹,强制删除 19 | rm -rf app/link_obj app/dep nginx 20 | rm -rf signal/*.gch app/*.gch 21 | 22 | -------------------------------------------------------------------------------- /ab_plc_cip_net/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef __SOCKET_H_ 2 | #define __SOCKET_H_ 3 | 4 | #include "utill.h" 5 | 6 | int socket_send_data(int fd, void* ptr, int nbytes); 7 | int socket_recv_data(int fd, void* ptr, int nbytes); 8 | int socket_recv_data_one_loop(int fd, void* ptr, int nbytes); 9 | int socket_open_tcp_client_socket(char* ip, short port); 10 | void socket_close_tcp_socket(int sockFd); 11 | 12 | #endif //__SOCKET_H_ 13 | -------------------------------------------------------------------------------- /ab_plc_cip_net/ab_cip_private.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_AB_CIP_PRIVATE_H__ 2 | #define __H_AB_CIP_PRIVATE_H__ 3 | #include "typedef.h" 4 | 5 | /// 6 | /// 注册命令 7 | /// 8 | const byte g_registered_command[] = 9 | { 10 | 0x65, 0x00, // 注册请求 11 | 0x04, 0x00, // 命令数据长度(单位字节) 12 | 0x00, 0x00, 0x00, 0x00, // 会话句柄,初始值为0x00000000 13 | 0x00, 0x00, 0x00, 0x00, // 状态,初始值为0x00000000(状态好) 14 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 请求通信一方的说明 15 | 0x00, 0x00, 0x00, 0x00, // 选项,默认为0x00000000 16 | 0x01, 0x00, // 协议版本(0x0001) 17 | 0x00, 0x00 // 选项标记(0x0000 18 | }; 19 | 20 | // 会话句柄(由AB PLC生成) 21 | uint32 g_session; 22 | 23 | byte g_plc_slot; 24 | 25 | #endif //__H_AB_CIP_PRIVATE_H__ 26 | -------------------------------------------------------------------------------- /ab_plc_cip_net/ab_cip_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_AB_CIP_HELPER_H__ 2 | #define __H_AB_CIP_HELPER_H__ 3 | #include "utill.h" 4 | #include 5 | 6 | #define BUFFER_SIZE 1024 7 | #define HEAD_SIZE 24 8 | 9 | byte_array_info build_read_core_command(const char* address, int length); 10 | byte_array_info build_write_core_command(const char* address, ushort typeCode, int length, byte_array_info value); 11 | 12 | cip_error_code_e cip_analysis_read_byte(byte_array_info response, byte_array_info* ret); 13 | cip_error_code_e cip_analysis_write_byte(byte_array_info response); 14 | 15 | bool read_data_from_server(int fd, byte_array_info send, int* session); 16 | bool cip_read_response(int fd, byte_array_info* response); 17 | 18 | cip_error_code_e read_value(int fd, const char* address, int length, byte_array_info* out_bytes); 19 | cip_error_code_e write_value(int fd, const char* address, int length, ushort type_code, byte_array_info in_bytes); 20 | 21 | bool initialization_on_connect(int fd); 22 | 23 | #endif //__H_AB_CIP_HELPER_H__ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 wqliceman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ab_plc_cip_net/ab_plc_cip_net.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.33027.164 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ab_plc_cip_net", "ab_plc_cip_net.vcxproj", "{181055E5-C375-4C97-84A2-90CD807A17FD}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Debug|x64.ActiveCfg = Debug|x64 17 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Debug|x64.Build.0 = Debug|x64 18 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Debug|x86.ActiveCfg = Debug|Win32 19 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Debug|x86.Build.0 = Debug|Win32 20 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Release|x64.ActiveCfg = Release|x64 21 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Release|x64.Build.0 = Release|x64 22 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Release|x86.ActiveCfg = Release|Win32 23 | {181055E5-C375-4C97-84A2-90CD807A17FD}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {0313BC2F-57C8-4606-B430-4861FF084853} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /ab_plc_cip_net/typedef.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_TYPEDEF_H__ 2 | #define __H_TYPEDEF_H__ 3 | 4 | #include 5 | #include 6 | 7 | typedef unsigned char byte; 8 | typedef unsigned short ushort; 9 | typedef signed int int32; 10 | typedef unsigned int uint32; 11 | typedef long long int64; 12 | typedef unsigned long long uint64; 13 | 14 | typedef enum _tag_cip_error_code 15 | { 16 | CIP_ERROR_CODE_SUCCESS = 0, // 成功 17 | CIP_ERROR_CODE_FAILED = 1, // 错误 18 | CIP_ERROR_CODE_MALLOC_FAILED = 2, // 内存分配错误 19 | CIP_ERROR_CODE_INVALID_PARAMETER = 3, // 参数错误 20 | CIP_ERROR_CODE_UNKOWN = 99, // 未知错误 21 | } cip_error_code_e; 22 | 23 | typedef enum _tag_cip_type 24 | { 25 | CIP_Type_Bool = 0xC1, // bool型数据,一个字节长度 26 | CIP_Type_Byte = 0xC2, // byte型数据,一个字节长度,SINT 27 | CIP_Type_Word = 0xC3, // 整型,两个字节长度,INT 28 | CIP_Type_DWord = 0xC4, // 长整型,四个字节长度,DINT 29 | CIP_Type_LInt = 0xC5, // 特长整型,8个字节,LINT 30 | CIP_Type_USInt = 0xC6, // Unsigned 8-bit integer, USINT 31 | CIP_Type_UInt = 0xC7, // Unsigned 16-bit integer, UINT 32 | CIP_Type_UDint = 0xC8, // Unsigned 32-bit integer, UDINT 33 | CIP_Type_ULint = 0xC9, // Unsigned 64-bit integer, ULINT 34 | CIP_Type_Real = 0xCA, // 实数数据,四个字节长度 35 | CIP_Type_Double = 0xCB, // 实数数据,八个字节的长度 36 | CIP_Type_Struct = 0xCC, // 结构体数据,不定长度 37 | CIP_Type_String = 0xCD, // 字符串数据内容 38 | CIP_Type_D1 = 0xD1, // Bit string, 8 bits, BYTE 39 | CIP_Type_D2 = 0xD2, // Bit string, 16-bits, WORD 40 | CIP_Type_D3 = 0xD3, // Bit string, 32 bits, DWORD 41 | CIP_Type_D4 = 0xD4, // Bit string, 64 bits LWORD 42 | CIP_Type_DA = 0xDA, // 43 | 44 | }cip_type_e; 45 | 46 | #endif // !__H_TYPEDEF_H__ -------------------------------------------------------------------------------- /ab_plc_cip_net/utill.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILL_H__ 2 | #define __UTILL_H__ 3 | 4 | #include "typedef.h" 5 | 6 | #define RELEASE_DATA(data) do { if ((data) != NULL) { free(data); (data) = NULL; } } while(0) 7 | 8 | typedef struct _tag_byte_array_info 9 | { 10 | byte* data; // 内容 11 | int length; // 长度 12 | cip_type_e type;// 数据类型 13 | } byte_array_info; 14 | 15 | typedef struct _tag_bool_array_info 16 | { 17 | bool* data; // 内容 18 | int length; // 长度 19 | cip_type_e type;// 数据类型 20 | } bool_array_info; 21 | 22 | void short2bytes(short i, byte* bytes); 23 | short bytes2short(byte* bytes); 24 | 25 | void ushort2bytes(ushort i, byte* bytes); 26 | ushort bytes2ushort(byte* bytes); 27 | 28 | void int2bytes(int32 i, byte* bytes); 29 | int32 bytes2int32(byte* bytes); 30 | 31 | void uint2bytes(uint32 i, byte* bytes); 32 | uint32 bytes2uint32(byte* bytes); 33 | 34 | void bigInt2bytes(int64 i, byte* bytes); 35 | int64 bytes2bigInt(byte* bytes); 36 | 37 | void ubigInt2bytes(uint64 i, byte* bytes); 38 | uint64 bytes2ubigInt(byte* bytes); 39 | 40 | void float2bytes(float i, byte* bytes); 41 | float bytes2float(byte* bytes); 42 | 43 | void double2bytes(double i, byte* bytes); 44 | double bytes2double(byte* bytes); 45 | 46 | int str_to_int(const char* address); 47 | void str_toupper(char* input); 48 | void str_tolower(char* input); 49 | int str_start_with(const char* origin, char* prefix); 50 | int str_end_with(const char* origin, char* end); 51 | 52 | uint32 htonf_(float value); 53 | float ntohf_(uint32 value); 54 | uint64 htond_(double value); 55 | double ntohd_(uint64 value); 56 | uint64 htonll_(uint64 Value); 57 | uint64 ntohll_(uint64 Value); 58 | 59 | #ifndef _WIN32 60 | char* itoa(unsigned long long value, char str[], int radix); 61 | #endif // !_WIN32 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /ab_plc_cip_net/ab_cip.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_AB_CIP_H__ 2 | #define __H_AB_CIP_H__ 3 | 4 | #include "typedef.h" 5 | 6 | byte get_plc_slot(); 7 | void set_plc_slot(byte slot); 8 | 9 | bool ab_cip_connect(char* ip_addr, int port, int slot, int* fd); 10 | bool ab_cip_disconnect(int fd); 11 | 12 | // read 13 | cip_error_code_e ab_cip_read_bool(int fd, const char* address, bool* val); 14 | cip_error_code_e ab_cip_read_short(int fd, const char* address, short* val); 15 | cip_error_code_e ab_cip_read_ushort(int fd, const char* address, ushort* val); 16 | cip_error_code_e ab_cip_read_int32(int fd, const char* address, int32* val); 17 | cip_error_code_e ab_cip_read_uint32(int fd, const char* address, uint32* val); 18 | cip_error_code_e ab_cip_read_int64(int fd, const char* address, int64* val); 19 | cip_error_code_e ab_cip_read_uint64(int fd, const char* address, uint64* val); 20 | cip_error_code_e ab_cip_read_float(int fd, const char* address, float* val); 21 | cip_error_code_e ab_cip_read_double(int fd, const char* address, double* val); 22 | cip_error_code_e ab_cip_read_string(int fd, const char* address, int* length, char** val); // need free val 23 | 24 | // write 25 | cip_error_code_e ab_cip_write_bool(int fd, const char* address, bool val); 26 | cip_error_code_e ab_cip_write_short(int fd, const char* address, short val); 27 | cip_error_code_e ab_cip_write_ushort(int fd, const char* address, ushort val); 28 | cip_error_code_e ab_cip_write_int32(int fd, const char* address, int32 val); 29 | cip_error_code_e ab_cip_write_uint32(int fd, const char* address, uint32 val); 30 | cip_error_code_e ab_cip_write_int64(int fd, const char* address, int64 val); 31 | cip_error_code_e ab_cip_write_uint64(int fd, const char* address, uint64 val); 32 | cip_error_code_e ab_cip_write_float(int fd, const char* address, float val); 33 | cip_error_code_e ab_cip_write_double(int fd, const char* address, double val); 34 | #if false 35 | // Not supported at the moment, there is a bug 36 | cip_error_code_e ab_cip_write_string(int fd, const char* address, int length, const char* val); 37 | #endif 38 | 39 | #endif //__H_AB_CIP_H__ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | 2 | #.PHONY:all clean 3 | 4 | ifeq ($(DEBUG),true) 5 | #-g是生成调试信息。GNU调试器可以利用该信息 6 | CC = gcc -g 7 | VERSION = debug 8 | else 9 | CC = gcc 10 | VERSION = release 11 | endif 12 | 13 | #CC = gcc 14 | 15 | # $(wildcard *.c)表示扫描当前目录下所有.c文件 16 | #SRCS = nginx.c ngx_conf.c 17 | SRCS = $(wildcard *.c) 18 | 19 | OBJS = $(SRCS:.c=.o) 20 | 21 | #把字符串中的.c替换为.d 22 | #DEPS = nginx.d ngx_conf.d 23 | DEPS = $(SRCS:.c=.d) 24 | 25 | #可以指定BIN文件的位置,addprefix是增加前缀函数 26 | #BIN = /mnt/hgfs/linux/nginx 27 | BIN := $(addprefix $(BUILD_ROOT)/,$(BIN)) 28 | 29 | #定义存放ojb文件的目录,目录统一到一个位置才方便后续链接,不然整到各个子目录去,不好链接 30 | #注意下边这个字符串,末尾不要有空格等否则会语法错误 31 | LINK_OBJ_DIR = $(BUILD_ROOT)/app/link_obj 32 | DEP_DIR = $(BUILD_ROOT)/app/dep 33 | 34 | #-p是递归创建目录,没有就创建,有就不需要创建了 35 | $(shell mkdir -p $(LINK_OBJ_DIR)) 36 | $(shell mkdir -p $(DEP_DIR)) 37 | 38 | OBJS := $(addprefix $(LINK_OBJ_DIR)/,$(OBJS)) 39 | DEPS := $(addprefix $(DEP_DIR)/,$(DEPS)) 40 | 41 | #找到目录中的所有.o文件(编译出来的) 42 | LINK_OBJ = $(wildcard $(LINK_OBJ_DIR)/*.o) 43 | #因为构建依赖关系时app目录下这个.o文件还没构建出来,所以LINK_OBJ是缺少这个.o的,我们 要把这个.o文件加进来 44 | LINK_OBJ += $(OBJS) 45 | 46 | #------------------------------------------------------------------------------------------------------- 47 | all:$(DEPS) $(OBJS) $(BIN) 48 | 49 | ifneq ("$(wildcard $(DEPS))","") #如果不为空,$(wildcard)是函数【获取匹配模式文件名】,这里 用于比较是否为"" 50 | include $(DEPS) 51 | endif 52 | 53 | #----------------------------------------------------------------1begin------------------ 54 | #$(BIN):$(OBJS) 55 | $(BIN):$(LINK_OBJ) 56 | @echo "------------------------build $(VERSION) mode--------------------------------!!!" 57 | 58 | ifeq ($(BUILD_SO), true) 59 | # gcc -o 是生成so 60 | $(CC) -fPIC -shared -o $@.so $^ 61 | else 62 | # gcc -o 是生成可执行文件 63 | $(CC) -o $@ $^ 64 | endif 65 | 66 | #----------------------------------------------------------------1end------------------- 67 | 68 | 69 | #----------------------------------------------------------------2begin----------------- 70 | #%.o:%.c 71 | $(LINK_OBJ_DIR)/%.o:%.c 72 | #$(CC) -o $@ -c $^ 73 | $(CC) -I$(INCLUDE_PATH) -o $@ -c $(filter %.c,$^) 74 | #----------------------------------------------------------------2end------------------- 75 | 76 | 77 | 78 | #----------------------------------------------------------------3begin----------------- 79 | $(DEP_DIR)/%.d:%.c 80 | #gcc -MM $^ > $@ 81 | echo -n $(LINK_OBJ_DIR)/ > $@ 82 | # gcc -MM $^ | sed 's/^/$(LINK_OBJ_DIR)&/g' > $@ 83 | # >>表示追加 84 | gcc -I$(INCLUDE_PATH) -MM $^ >> $@ 85 | #----------------------------------------------------------------3begin----------------- 86 | 87 | 88 | 89 | #----------------------------------------------------------------nbegin----------------- 90 | clean: 91 | rm -f $(BIN) $(OBJS) $(DEPS) *.gch 92 | #----------------------------------------------------------------nend------------------ 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /ab_plc_cip_net/socket.c: -------------------------------------------------------------------------------- 1 | #include "socket.h" 2 | #include 3 | #include 4 | 5 | #ifdef _WIN32 6 | #include 7 | #include 8 | #include 9 | #pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ 10 | #else 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #endif 17 | 18 | int socket_send_data(int fd, void* buf, int nbytes) 19 | { 20 | int nleft, nwritten; 21 | char* ptr = (char*)buf; 22 | 23 | if (fd < 0) 24 | return -1; 25 | 26 | nleft = nbytes; 27 | while (nleft > 0) 28 | { 29 | nwritten = send(fd, ptr, nleft, 0); 30 | if (nwritten <= 0) 31 | { 32 | if (errno == EINTR) 33 | continue; 34 | else 35 | return -1; 36 | } 37 | else 38 | { 39 | nleft -= nwritten; 40 | ptr += nwritten; 41 | } 42 | } 43 | 44 | return (nbytes - nleft); 45 | } 46 | 47 | int socket_recv_data(int fd, void* buf, int nbytes) 48 | { 49 | int nleft, nread; 50 | char* ptr = (char*)buf; 51 | 52 | if (fd < 0) 53 | return -1; 54 | 55 | nleft = nbytes; 56 | while (nleft > 0) 57 | { 58 | nread = recv(fd, ptr, nleft, 0); 59 | if (nread == 0) 60 | { 61 | break; 62 | } 63 | else if (nread < 0) 64 | { 65 | if (errno == EINTR) 66 | continue; 67 | else 68 | return -1; 69 | } 70 | else 71 | { 72 | nleft -= nread; 73 | ptr += nread; 74 | } 75 | } 76 | 77 | return (nbytes - nleft); 78 | } 79 | 80 | int socket_recv_data_one_loop(int fd, void* buf, int nbytes) 81 | { 82 | int nleft, nread; 83 | char* ptr = (char*)buf; 84 | 85 | if (fd < 0) 86 | return -1; 87 | 88 | nleft = nbytes; 89 | while (nleft > 0) 90 | { 91 | nread = recv(fd, ptr, nleft, 0); 92 | if (nread == 0) 93 | { 94 | break; 95 | } 96 | else if (nread < 0) 97 | { 98 | if (errno == EINTR) 99 | continue; 100 | else 101 | return -1; 102 | } 103 | else 104 | { 105 | nleft -= nread; 106 | ptr += nread; 107 | 108 | // 目前只接收一次 109 | break; 110 | } 111 | } 112 | 113 | return (nbytes - nleft); 114 | } 115 | 116 | int socket_open_tcp_client_socket(char* destIp, short destPort) 117 | { 118 | int sockFd = 0; 119 | struct sockaddr_in serverAddr; 120 | int ret; 121 | 122 | sockFd = (int)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 123 | 124 | if (sockFd < 0) 125 | { 126 | return -1; 127 | #pragma warning(disable : 4996) 128 | } 129 | 130 | memset((char*)&serverAddr, 0, sizeof(serverAddr)); 131 | serverAddr.sin_family = AF_INET; 132 | serverAddr.sin_addr.s_addr = inet_addr(destIp); 133 | serverAddr.sin_port = (uint16_t)htons((uint16_t)destPort); 134 | 135 | ret = connect(sockFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); 136 | 137 | if (ret != 0) 138 | { 139 | socket_close_tcp_socket(sockFd); 140 | sockFd = -1; 141 | } 142 | 143 | #ifdef _WIN32 144 | int timeout = 5000; // 5s 145 | ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); 146 | ret = setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); 147 | #else 148 | struct timeval timeout = { 5, 0 }; // 3s 149 | ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); 150 | ret = setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); 151 | #endif 152 | 153 | return sockFd; 154 | } 155 | 156 | void socket_close_tcp_socket(int sockFd) 157 | { 158 | if (sockFd > 0) 159 | { 160 | #ifdef _WIN32 161 | closesocket(sockFd); 162 | #else 163 | close(sockFd); 164 | #endif 165 | } 166 | } 167 | 168 | void tinet_ntoa(char* ipstr, unsigned int ip) 169 | { 170 | sprintf(ipstr, "%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24); 171 | } -------------------------------------------------------------------------------- /ab_plc_cip_net/main.c: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #include 3 | #endif 4 | #include 5 | #include 6 | #pragma warning(disable : 4996) 7 | 8 | #define GET_RESULT(ret) { if (ret != 0) failed_count++;} 9 | 10 | #include "ab_cip.h" 11 | 12 | int main(int argc, char** argv) 13 | { 14 | #ifdef _WIN32 15 | WSADATA wsa; 16 | if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) 17 | { 18 | return -1; 19 | } 20 | #endif 21 | 22 | char* plc_ip = "172.18.224.1"; 23 | int plc_port = 44818; 24 | if (argc > 1) 25 | { 26 | plc_ip = argv[1]; 27 | plc_port = atoi(argv[2]); 28 | } 29 | 30 | int fd = -1; 31 | int slot = 0; 32 | bool ret = ab_cip_connect(plc_ip, plc_port, 0, &fd); 33 | if (ret && fd > 0) 34 | { 35 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 36 | 37 | const int TEST_COUNT = 5000; 38 | const int TEST_SLEEP_TIME = 1000; 39 | int failed_count = 0; 40 | char address[50] = { 0 }; 41 | int i = 0; 42 | 43 | for (i = 0; i < TEST_COUNT; i++) 44 | { 45 | printf("==============Test count: %d==============\n", i + 1); 46 | bool all_success = false; 47 | ////////////////////////////////////////////////////////////////////////// 48 | bool val = true; 49 | strcpy(address, "E"); 50 | ret = ab_cip_write_bool(fd, address, val); 51 | printf("Write\t %s \tbool:\t %d, \tret: %d\n", address, val, ret); 52 | GET_RESULT(ret); 53 | 54 | val = false; 55 | ret = ab_cip_read_bool(fd, address, &val); 56 | printf("Read\t %s \tbool:\t %d\n", address, val); 57 | GET_RESULT(ret); 58 | 59 | ////////////////////////////////////////////////////////////////////////// 60 | short w_s_val = -23; 61 | strcpy(address, "A"); 62 | ret = ab_cip_write_short(fd, address, w_s_val); 63 | printf("Write\t %s \tshort:\t %d, \tret: %d\n", address, w_s_val, ret); 64 | GET_RESULT(ret); 65 | 66 | short s_val = 0; 67 | ret = ab_cip_read_short(fd, address, &s_val); 68 | printf("Read\t %s \tshort:\t %d\n", address, s_val); 69 | GET_RESULT(ret); 70 | 71 | ////////////////////////////////////////////////////////////////////////// 72 | ushort w_us_val = 255; 73 | strcpy(address, "A"); 74 | ret = ab_cip_write_ushort(fd, address, w_us_val); 75 | printf("Write\t %s \tushort:\t %d, \tret: %d\n", address, w_us_val, ret); 76 | GET_RESULT(ret); 77 | 78 | ushort us_val = 0; 79 | ret = ab_cip_read_ushort(fd, address, &us_val); 80 | printf("Read\t %s \tushort:\t %d\n", address, us_val); 81 | GET_RESULT(ret); 82 | 83 | ////////////////////////////////////////////////////////////////////////// 84 | int32 w_i_val = -12345; 85 | strcpy(address, "B"); 86 | ret = ab_cip_write_int32(fd, address, w_i_val); 87 | printf("Write\t %s \tint32:\t %d, \tret: %d\n", address, w_i_val, ret); 88 | GET_RESULT(ret); 89 | 90 | int i_val = 0; 91 | ret = ab_cip_read_int32(fd, address, &i_val); 92 | printf("Read\t %s \tint32:\t %d\n", address, i_val); 93 | GET_RESULT(ret); 94 | 95 | ////////////////////////////////////////////////////////////////////////// 96 | uint32 w_ui_val = 22345; 97 | ret = ab_cip_write_uint32(fd, address, w_ui_val); 98 | printf("Write\t %s \tuint32:\t %d, \tret: %d\n", address, w_ui_val, ret); 99 | GET_RESULT(ret); 100 | 101 | uint32 ui_val = 0; 102 | ret = ab_cip_read_uint32(fd, address, &ui_val); 103 | printf("Read\t %s \tuint32:\t %d\n", address, ui_val); 104 | GET_RESULT(ret); 105 | 106 | ////////////////////////////////////////////////////////////////////////// 107 | #if true 108 | int64 w_i64_val = -333334554; 109 | strcpy(address, "N"); 110 | ret = ab_cip_write_int64(fd, address, w_i64_val); 111 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_i64_val, ret); 112 | GET_RESULT(ret); 113 | 114 | int64 i64_val = 0; 115 | ret = ab_cip_read_int64(fd, address, &i64_val); 116 | printf("Read\t %s \tint64:\t %lld\n", address, i64_val); 117 | GET_RESULT(ret); 118 | 119 | #endif 120 | ////////////////////////////////////////////////////////////////////////// 121 | uint64 w_ui64_val = 4333334554; 122 | strcpy(address, "N"); 123 | ret = ab_cip_write_uint64(fd, address, w_ui64_val); 124 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_ui64_val, ret); 125 | GET_RESULT(ret); 126 | 127 | int64 ui64_val = 0; 128 | ret = ab_cip_read_uint64(fd, address, &ui64_val); 129 | printf("Read\t %s \tuint64:\t %lld\n", address, ui64_val); 130 | GET_RESULT(ret); 131 | 132 | ////////////////////////////////////////////////////////////////////////// 133 | float w_f_val = 32.454f; 134 | strcpy(address, "C"); 135 | ret = ab_cip_write_float(fd, address, w_f_val); 136 | printf("Write\t %s \tfloat:\t %f, \tret: %d\n", address, w_f_val, ret); 137 | GET_RESULT(ret); 138 | 139 | float f_val = 0; 140 | ret = ab_cip_read_float(fd, address, &f_val); 141 | printf("Read\t %s \tfloat:\t %f\n", address, f_val); 142 | GET_RESULT(ret); 143 | 144 | ////////////////////////////////////////////////////////////////////////// 145 | #if true 146 | // this function NEED TEST 147 | double w_d_val = -12345.6789; 148 | strcpy(address, "A1"); 149 | ret = ab_cip_write_double(fd, address, w_d_val); 150 | printf("Write\t %s \tdouble:\t %lf, \tret: %d\n", address, w_d_val, ret); 151 | GET_RESULT(ret); 152 | 153 | double d_val = 0; 154 | ret = ab_cip_read_double(fd, address, &d_val); 155 | printf("Read\t %s \tdouble:\t %lf\n", address, d_val); 156 | GET_RESULT(ret); 157 | 158 | #endif 159 | ////////////////////////////////////////////////////////////////////////// 160 | #if true 161 | int length = 0; 162 | strcpy(address, "F"); 163 | char* str_val = NULL; 164 | ret = ab_cip_read_string(fd, address, &length, &str_val); 165 | printf("Read\t %s \tstring:\t %s\n", address, str_val); 166 | free(str_val); 167 | GET_RESULT(ret); 168 | #endif 169 | 170 | #ifdef _WIN32 171 | Sleep(TEST_SLEEP_TIME); 172 | #else 173 | usleep(TEST_SLEEP_TIME * 1000); 174 | #endif 175 | } 176 | 177 | printf("All Failed count: %d\n", failed_count); 178 | 179 | ab_cip_disconnect(fd); 180 | system("pause"); 181 | } 182 | 183 | #ifdef _WIN32 184 | WSACleanup(); 185 | #endif 186 | } -------------------------------------------------------------------------------- /ab_plc_cip_net/utill.c: -------------------------------------------------------------------------------- 1 | #include "utill.h" 2 | #include 3 | #include 4 | 5 | #ifdef _WIN32 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | #define _WS2_32_WINSOCK_SWAP_LONG(l) \ 12 | ((((l) >> 24) & 0x000000FFL) | \ 13 | (((l) >> 8) & 0x0000FF00L) | \ 14 | (((l) << 8) & 0x00FF0000L) | \ 15 | (((l) << 24) & 0xFF000000L)) 16 | 17 | #define _WS2_32_WINSOCK_SWAP_LONGLONG(l) \ 18 | ((((l) >> 56) & 0x00000000000000FFLL) | \ 19 | (((l) >> 40) & 0x000000000000FF00LL) | \ 20 | (((l) >> 24) & 0x0000000000FF0000LL) | \ 21 | (((l) >> 8) & 0x00000000FF000000LL) | \ 22 | (((l) << 8) & 0x000000FF00000000LL) | \ 23 | (((l) << 24) & 0x0000FF0000000000LL) | \ 24 | (((l) << 40) & 0x00FF000000000000LL) | \ 25 | (((l) << 56) & 0xFF00000000000000LL)) 26 | 27 | void short2bytes(short i, byte* bytes) 28 | { 29 | bytes[0] = (byte)(0xff & i); 30 | bytes[1] = (byte)((0xff00 & i) >> 8); 31 | } 32 | 33 | short bytes2short(byte* bytes) 34 | { 35 | short iRetVal = bytes[0] & 0xFF; 36 | iRetVal |= (((short)bytes[1] << 8) & 0xFF00); 37 | return iRetVal; 38 | } 39 | 40 | void ushort2bytes(ushort i, byte* bytes) 41 | { 42 | bytes[0] = (byte)(0xff & i); 43 | bytes[1] = (byte)((0xff00 & i) >> 8); 44 | } 45 | 46 | ushort bytes2ushort(byte* bytes) 47 | { 48 | ushort iRetVal = bytes[0] & 0xFF; 49 | iRetVal |= (((ushort)bytes[1] << 8) & 0xFF00); 50 | return iRetVal; 51 | } 52 | 53 | void int2bytes(int32 i, byte* bytes) 54 | { 55 | bytes[0] = (byte)(0xff & i); 56 | bytes[1] = (byte)((0xff00 & i) >> 8); 57 | bytes[2] = (byte)((0xff0000 & i) >> 16); 58 | bytes[3] = (byte)((0xff000000 & i) >> 24); 59 | } 60 | 61 | int32 bytes2int32(byte* bytes) 62 | { 63 | int32 iRetVal = bytes[0] & 0xFF; 64 | iRetVal |= (((int32)bytes[1] << 8) & 0xFF00); 65 | iRetVal |= (((int32)bytes[2] << 16) & 0xFF0000); 66 | iRetVal |= (((int32)bytes[3] << 24) & 0xFF000000); 67 | return iRetVal; 68 | } 69 | 70 | void uint2bytes(uint32 i, byte* bytes) 71 | { 72 | bytes[0] = (byte)(0xff & i); 73 | bytes[1] = (byte)((0xff00 & i) >> 8); 74 | bytes[2] = (byte)((0xff0000 & i) >> 16); 75 | bytes[3] = (byte)((0xff000000 & i) >> 24); 76 | } 77 | 78 | uint32 bytes2uint32(byte* bytes) 79 | { 80 | uint32 iRetVal = bytes[0] & 0xFF; 81 | iRetVal |= (((uint32)bytes[1] << 8) & 0xFF00); 82 | iRetVal |= (((uint32)bytes[2] << 16) & 0xFF0000); 83 | iRetVal |= (((uint32)bytes[3] << 24) & 0xFF000000); 84 | return iRetVal; 85 | } 86 | 87 | void bigInt2bytes(int64 i, byte* bytes) 88 | { 89 | for (int j = 0; j < 8; j++) { 90 | bytes[j] = (byte)((i >> (j * 8)) & 0xFF); 91 | } 92 | } 93 | 94 | int64 bytes2bigInt(byte* bytes) 95 | { 96 | int64 iRetVal = bytes[0] & 0xFF; 97 | iRetVal |= (((int64)bytes[1] << 8) & 0xFF00); 98 | iRetVal |= (((int64)bytes[2] << 16) & 0xFF0000); 99 | iRetVal |= (((int64)bytes[3] << 24) & 0xFF000000); 100 | iRetVal |= (((int64)bytes[4] << 32) & 0xFF00000000); 101 | iRetVal |= (((int64)bytes[5] << 40) & 0xFF0000000000); 102 | iRetVal |= (((int64)bytes[6] << 48) & 0xFF000000000000); 103 | iRetVal |= (((int64)bytes[7] << 56) & 0xFF00000000000000); 104 | return iRetVal; 105 | } 106 | 107 | void ubigInt2bytes(uint64 i, byte* bytes) 108 | { 109 | for (int j = 0; j < 8; j++) { 110 | bytes[j] = (byte)((i >> (j * 8)) & 0xFF); 111 | } 112 | } 113 | 114 | uint64 bytes2ubigInt(byte* bytes) 115 | { 116 | uint64 iRetVal = bytes[0] & 0xFF; 117 | iRetVal |= (((uint64)bytes[1] << 8) & 0xFF00); 118 | iRetVal |= (((uint64)bytes[2] << 16) & 0xFF0000); 119 | iRetVal |= (((uint64)bytes[3] << 24) & 0xFF000000); 120 | iRetVal |= (((uint64)bytes[4] << 32) & 0xFF00000000); 121 | iRetVal |= (((uint64)bytes[5] << 40) & 0xFF0000000000); 122 | iRetVal |= (((uint64)bytes[6] << 48) & 0xFF000000000000); 123 | iRetVal |= (((uint64)bytes[7] << 56) & 0xFF00000000000000); 124 | return iRetVal; 125 | } 126 | 127 | void float2bytes(float i, byte* bytes) 128 | { 129 | int size = 4; 130 | int temp = *(int*)&i; 131 | int2bytes(temp, bytes); 132 | } 133 | 134 | float bytes2float(byte* bytes) 135 | { 136 | int temp = bytes2int32(bytes); 137 | return *(float*)&temp; 138 | } 139 | 140 | void double2bytes(double i, byte* bytes) 141 | { 142 | int64 temp = *(int64*)&i; 143 | bigInt2bytes(temp, bytes); 144 | } 145 | 146 | double bytes2double(byte* bytes) 147 | { 148 | int64 temp = bytes2bigInt(bytes); 149 | return *(double*)&temp; 150 | } 151 | 152 | int str_to_int(const char* address) 153 | { 154 | int ret = 0; 155 | ret = (int)strtol(address, NULL, 10); 156 | return ret; 157 | } 158 | 159 | void str_toupper(char* input) 160 | { 161 | if (input == NULL) 162 | return; 163 | 164 | int32 len = strlen(input), i = 0; 165 | for (; i < len; i++) 166 | input[i] = toupper(input[i]); 167 | } 168 | 169 | void str_tolower(char* input) 170 | { 171 | if (input == NULL) 172 | return; 173 | 174 | int32 len = strlen(input), i = 0; 175 | for (; i < len; i++) 176 | input[i] = tolower(input[i]); 177 | } 178 | 179 | /** 180 | * ×字符串origin以字符串prefix开头,返回0;否则返回1;异常返回-1 181 | */ 182 | int str_start_with(const char* origin, char* prefix) 183 | { 184 | if (origin == NULL || 185 | prefix == NULL || 186 | strlen(prefix) > strlen(origin)) 187 | { 188 | return -1; 189 | } 190 | 191 | int n = strlen(prefix), i; 192 | for (i = 0; i < n; i++) 193 | { 194 | if (origin[i] != prefix[i]) 195 | { 196 | return 1; 197 | } 198 | } 199 | return 0; 200 | } 201 | 202 | /** 203 | * 字符串origin以字符串end结尾,返回0;否则返回1;异常返回-1 204 | */ 205 | int str_end_with(const char* origin, char* end) 206 | { 207 | if (origin == NULL || 208 | end == NULL || 209 | strlen(end) > strlen(origin)) 210 | { 211 | return -1; 212 | } 213 | 214 | int n = strlen(end); 215 | int m = strlen(origin); 216 | int i; 217 | for (i = 0; i < n; i++) 218 | { 219 | if (origin[m - i - 1] != end[n - i - 1]) 220 | return 1; 221 | } 222 | return 0; 223 | } 224 | 225 | uint32 htonf_(float value) 226 | { 227 | uint32 Tempval; 228 | uint32 Retval; 229 | Tempval = *(uint32*)(&value); 230 | Retval = _WS2_32_WINSOCK_SWAP_LONG(Tempval); 231 | return Retval; 232 | } 233 | 234 | float ntohf_(uint32 value) 235 | { 236 | const uint32 Tempval = _WS2_32_WINSOCK_SWAP_LONG(value); 237 | float Retval; 238 | *((uint32*)&Retval) = Tempval; 239 | return Retval; 240 | } 241 | 242 | uint64 htond_(double value) 243 | { 244 | uint64 Tempval; 245 | uint64 Retval; 246 | Tempval = *(uint64*)(&value); 247 | Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Tempval); 248 | return Retval; 249 | } 250 | 251 | double ntohd_(uint64 value) 252 | { 253 | const uint64 Tempval = _WS2_32_WINSOCK_SWAP_LONGLONG(value); 254 | double Retval; 255 | *((uint64*)&Retval) = Tempval; 256 | return Retval; 257 | } 258 | 259 | uint64 htonll_(uint64 Value) 260 | { 261 | const uint64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Value); 262 | return Retval; 263 | } 264 | 265 | uint64 ntohll_(uint64 Value) 266 | { 267 | const uint64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Value); 268 | return Retval; 269 | } 270 | 271 | #ifndef _WIN32 272 | /* 273 | ============= 274 | itoa 275 | 276 | Convert integer to string 277 | 278 | PARAMS: 279 | - value A 64-bit number to convert 280 | - str Destination buffer; should be 66 characters long for radix2, 24 - radix8, 22 - radix10, 18 - radix16. 281 | - radix Radix must be in range -36 .. 36. Negative values used for signed numbers. 282 | ============= 283 | */ 284 | 285 | char* itoa(unsigned long long value, char str[], int radix) 286 | { 287 | char buf[66]; 288 | char* dest = buf + sizeof(buf); 289 | bool sign = false; 290 | 291 | if (value == 0) 292 | { 293 | memcpy(str, "0", 2); 294 | return str; 295 | } 296 | 297 | if (radix < 0) 298 | { 299 | radix = -radix; 300 | if ((long long)value < 0) 301 | { 302 | value = -value; 303 | sign = true; 304 | } 305 | } 306 | 307 | *--dest = '\0'; 308 | 309 | while (value) 310 | { 311 | *--dest = '0' + (value % radix); 312 | if (*dest > '9') 313 | *dest += 'A' - '9' - 1; 314 | value /= radix; 315 | } 316 | 317 | if (sign) 318 | *--dest = '-'; 319 | 320 | memcpy(str, dest, buf + sizeof(buf) - dest); 321 | return str; 322 | } 323 | #endif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | 365 | *.d 366 | *.o -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English README](README_EN.md) 2 | 3 | ## 程序整体介绍 4 | 5 | - 项目名称:ab_plc_cip_net 6 | - 开发语言:C语言 7 | - 支持操作系统:windows/linux 8 | - 测试设备:模拟AB-CIP 9 | 10 | 目前实现功能,实现罗克韦尔AB-PLC通讯类,采用CIP(EtherNet/IP)协议实现,需要在PLC侧先的以太网模块先进行配置。 11 | 12 | #### 头文件 13 | 14 | ```c 15 | #include "ab_cip.h" //协议提供方法接口 16 | #include "typedef.h" //部分类型宏定义 17 | ``` 18 | 19 | #### 连接属性 20 | 21 | - port: 端口号,通常为44818 22 | - plc_type: plc 型号,适用1756 ControlLogix, 1756 GuardLogix, 1769 CompactLogix, 1769 Compact GuardLogix, 1789SoftLogix, 5069 CompactLogix, 5069 Compact GuardLogix, Studio 5000 Logix Emulate等型号 23 | 24 | #### PLC地址分类 25 | 26 | 支持使用标签的形式进行读写操作(目前实现方法有限,未完全实现所有协议功能) 27 | 28 | ## 实现方法 29 | 30 | #### 1.连接PLC设备 31 | 32 | ```c 33 | byte get_plc_slot(); // 获取PLC槽位 34 | void set_plc_slot(byte slot); // 设置PLC槽位 35 | 36 | bool ab_cip_connect(char* ip_addr, int port, int slot, int* fd); // 连接PLC设备 37 | bool ab_cip_disconnect(int fd); // 断开与PLC的连接 38 | ``` 39 | 40 | #### 2.读取数据 41 | 42 | ```c 43 | cip_error_code_e ab_cip_read_bool(int fd, const char* address, bool* val); // 读取布尔值 44 | cip_error_code_e ab_cip_read_short(int fd, const char* address, short* val); // 读取短整型 45 | cip_error_code_e ab_cip_read_ushort(int fd, const char* address, ushort* val); // 读取无符号短整型 46 | cip_error_code_e ab_cip_read_int32(int fd, const char* address, int32* val); // 读取32位整型 47 | cip_error_code_e ab_cip_read_uint32(int fd, const char* address, uint32* val); // 读取无符号32位整型 48 | cip_error_code_e ab_cip_read_int64(int fd, const char* address, int64* val); // 读取64位整型 49 | cip_error_code_e ab_cip_read_uint64(int fd, const char* address, uint64* val); // 读取无符号64位整型 50 | cip_error_code_e ab_cip_read_float(int fd, const char* address, float* val); // 读取浮点型 51 | cip_error_code_e ab_cip_read_double(int fd, const char* address, double* val); // 读取双精度浮点型 52 | cip_error_code_e ab_cip_read_string(int fd, const char* address, int* length, char** val); // 读取字符串(需要释放val内存) 53 | ``` 54 | 55 | #### 3.写入数据 56 | 57 | ```c 58 | cip_error_code_e ab_cip_write_bool(int fd, const char* address, bool val); // 写入布尔值 59 | cip_error_code_e ab_cip_write_short(int fd, const char* address, short val); // 写入短整型 60 | cip_error_code_e ab_cip_write_ushort(int fd, const char* address, ushort val); // 写入无符号短整型 61 | cip_error_code_e ab_cip_write_int32(int fd, const char* address, int32 val); // 写入32位整型 62 | cip_error_code_e ab_cip_write_uint32(int fd, const char* address, uint32 val); // 写入无符号32位整型 63 | cip_error_code_e ab_cip_write_int64(int fd, const char* address, int64 val); // 写入64位整型 64 | cip_error_code_e ab_cip_write_uint64(int fd, const char* address, uint64 val); // 写入无符号64位整型 65 | cip_error_code_e ab_cip_write_float(int fd, const char* address, float val); // 写入浮点型 66 | cip_error_code_e ab_cip_write_double(int fd, const char* address, double val); // 写入双精度浮点型 67 | // 暂时不支持,存在bug 68 | cip_error_code_e ab_cip_write_string(int fd, const char* address, int length, const char* val); // 写入字符串 69 | ``` 70 | 71 | ## 使用样例 72 | 73 | 完整样例参见代码中**main.c**文件,如下提供主要代码和使用方法: 74 | 75 | 读取地址,格式为"**F**","**D**" 76 | 77 | ```c 78 | #ifdef _WIN32 79 | #include 80 | #endif 81 | #include 82 | #include 83 | #pragma warning(disable : 4996) 84 | 85 | #define GET_RESULT(ret) { if (ret != 0) failed_count++;} 86 | 87 | #include "ab_cip.h" 88 | 89 | int main(int argc, char** argv) 90 | { 91 | #ifdef _WIN32 92 | WSADATA wsa; 93 | if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) 94 | { 95 | return -1; 96 | } 97 | #endif 98 | 99 | char* plc_ip = "127.0.0.1"; 100 | int plc_port = 44818; 101 | if (argc > 1) 102 | { 103 | plc_ip = argv[1]; 104 | plc_port = atoi(argv[2]); 105 | } 106 | 107 | int fd = -1; 108 | int slot = 0; 109 | bool ret = ab_cip_connect(plc_ip, plc_port, 0, &fd); 110 | if (ret && fd > 0) 111 | { 112 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 113 | 114 | const int TEST_COUNT = 5000; 115 | const int TEST_SLEEP_TIME = 1000; 116 | int failed_count = 0; 117 | char address[50] = { 0 }; 118 | int i = 0; 119 | 120 | for (i = 0; i < TEST_COUNT; i++) 121 | { 122 | printf("==============Test count: %d==============\n", i + 1); 123 | bool all_success = false; 124 | ////////////////////////////////////////////////////////////////////////// 125 | bool val = true; 126 | strcpy(address, "E"); 127 | ret = ab_cip_write_bool(fd, address, val); 128 | printf("Write\t %s \tbool:\t %d, \tret: %d\n", address, val, ret); 129 | GET_RESULT(ret); 130 | 131 | val = false; 132 | ret = ab_cip_read_bool(fd, address, &val); 133 | printf("Read\t %s \tbool:\t %d\n", address, val); 134 | GET_RESULT(ret); 135 | 136 | ////////////////////////////////////////////////////////////////////////// 137 | short w_s_val = 23; 138 | strcpy(address, "A"); 139 | ret = ab_cip_write_short(fd, address, w_s_val); 140 | printf("Write\t %s \tshort:\t %d, \tret: %d\n", address, w_s_val, ret); 141 | GET_RESULT(ret); 142 | 143 | short s_val = 0; 144 | ret = ab_cip_read_short(fd, address, &s_val); 145 | printf("Read\t %s \tshort:\t %d\n", address, s_val); 146 | GET_RESULT(ret); 147 | 148 | ////////////////////////////////////////////////////////////////////////// 149 | ushort w_us_val = 255; 150 | strcpy(address, "A"); 151 | ret = ab_cip_write_ushort(fd, address, w_us_val); 152 | printf("Write\t %s \tushort:\t %d, \tret: %d\n", address, w_us_val, ret); 153 | GET_RESULT(ret); 154 | 155 | ushort us_val = 0; 156 | ret = ab_cip_read_ushort(fd, address, &us_val); 157 | printf("Read\t %s \tushort:\t %d\n", address, us_val); 158 | GET_RESULT(ret); 159 | 160 | ////////////////////////////////////////////////////////////////////////// 161 | int32 w_i_val = 12345; 162 | strcpy(address, "B"); 163 | ret = ab_cip_write_int32(fd, address, w_i_val); 164 | printf("Write\t %s \tint32:\t %d, \tret: %d\n", address, w_i_val, ret); 165 | GET_RESULT(ret); 166 | 167 | int i_val = 0; 168 | ret = ab_cip_read_int32(fd, address, &i_val); 169 | printf("Read\t %s \tint32:\t %d\n", address, i_val); 170 | GET_RESULT(ret); 171 | 172 | ////////////////////////////////////////////////////////////////////////// 173 | uint32 w_ui_val = 22345; 174 | ret = ab_cip_write_uint32(fd, address, w_ui_val); 175 | printf("Write\t %s \tuint32:\t %d, \tret: %d\n", address, w_ui_val, ret); 176 | GET_RESULT(ret); 177 | 178 | uint32 ui_val = 0; 179 | ret = ab_cip_read_uint32(fd, address, &ui_val); 180 | printf("Read\t %s \tuint32:\t %d\n", address, ui_val); 181 | GET_RESULT(ret); 182 | 183 | ////////////////////////////////////////////////////////////////////////// 184 | #if true 185 | int64 w_i64_val = 333334554; 186 | strcpy(address, "N"); 187 | ret = ab_cip_write_int64(fd, address, w_i64_val); 188 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_i64_val, ret); 189 | GET_RESULT(ret); 190 | 191 | int64 i64_val = 0; 192 | ret = ab_cip_read_int64(fd, address, &i64_val); 193 | printf("Read\t %s \tint64:\t %lld\n", address, i64_val); 194 | GET_RESULT(ret); 195 | 196 | #endif 197 | ////////////////////////////////////////////////////////////////////////// 198 | uint64 w_ui64_val = 4333334554; 199 | strcpy(address, "N"); 200 | ret = ab_cip_write_uint64(fd, address, w_ui64_val); 201 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_ui64_val, ret); 202 | GET_RESULT(ret); 203 | 204 | int64 ui64_val = 0; 205 | ret = ab_cip_read_uint64(fd, address, &ui64_val); 206 | printf("Read\t %s \tuint64:\t %lld\n", address, ui64_val); 207 | GET_RESULT(ret); 208 | 209 | ////////////////////////////////////////////////////////////////////////// 210 | float w_f_val = 32.454f; 211 | strcpy(address, "C"); 212 | ret = ab_cip_write_float(fd, address, w_f_val); 213 | printf("Write\t %s \tfloat:\t %f, \tret: %d\n", address, w_f_val, ret); 214 | GET_RESULT(ret); 215 | 216 | float f_val = 0; 217 | ret = ab_cip_read_float(fd, address, &f_val); 218 | printf("Read\t %s \tfloat:\t %f\n", address, f_val); 219 | GET_RESULT(ret); 220 | 221 | ////////////////////////////////////////////////////////////////////////// 222 | #if true 223 | // this function NEED TEST 224 | double w_d_val = 12345.6789; 225 | strcpy(address, "Double"); 226 | ret = ab_cip_write_double(fd, address, w_d_val); 227 | printf("Write\t %s \tdouble:\t %lf, \tret: %d\n", address, w_d_val, ret); 228 | GET_RESULT(ret); 229 | 230 | double d_val = 0; 231 | ret = ab_cip_read_double(fd, address, &d_val); 232 | printf("Read\t %s \tdouble:\t %lf\n", address, d_val); 233 | GET_RESULT(ret); 234 | 235 | #endif 236 | ////////////////////////////////////////////////////////////////////////// 237 | #if true 238 | int length = 0; 239 | strcpy(address, "F"); 240 | char* str_val = NULL; 241 | 242 | ret = ab_cip_read_string(fd, address, &length, &str_val); 243 | printf("Read\t %s \tstring:\t %s\n", address, str_val); 244 | free(str_val); 245 | GET_RESULT(ret); 246 | #endif 247 | 248 | #ifdef _WIN32 249 | Sleep(TEST_SLEEP_TIME); 250 | #else 251 | usleep(TEST_SLEEP_TIME * 1000); 252 | #endif 253 | } 254 | 255 | printf("All Failed count: %d\n", failed_count); 256 | 257 | ab_cip_disconnect(fd); 258 | system("pause"); 259 | } 260 | 261 | #ifdef _WIN32 262 | WSACleanup(); 263 | #endif 264 | } 265 | ``` 266 | -------------------------------------------------------------------------------- /ab_plc_cip_net/ab_plc_cip_net.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {181055E5-C375-4C97-84A2-90CD807A17FD} 24 | Win32Proj 25 | melsec_mc_net 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | MultiByte 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | MultiByte 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)\$(Configuration)\ 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | NotUsing 89 | Level3 90 | Disabled 91 | true 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 93 | true 94 | pch.h 95 | 4966 96 | MultiThreadedDebug 97 | 98 | 99 | Console 100 | true 101 | 102 | 103 | 104 | 105 | NotUsing 106 | Level3 107 | Disabled 108 | true 109 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 110 | true 111 | pch.h 112 | 4966 113 | 114 | 115 | Console 116 | true 117 | 118 | 119 | 120 | 121 | NotUsing 122 | Level3 123 | MaxSpeed 124 | true 125 | true 126 | true 127 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 128 | true 129 | pch.h 130 | 4966 131 | MultiThreaded 132 | 133 | 134 | Console 135 | true 136 | true 137 | true 138 | 139 | 140 | 141 | 142 | NotUsing 143 | Level3 144 | MaxSpeed 145 | true 146 | true 147 | true 148 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 149 | true 150 | pch.h 151 | 4966 152 | 153 | 154 | Console 155 | true 156 | true 157 | true 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | ## Project Overview 2 | - Project Name: ab_plc_cip_net 3 | - Development Language: C 4 | - Supported Operating Systems: Windows, Linux 5 | - Test Device: Simulated AB-CIP 6 | 7 | The current implementation provides a Rockwell AB-PLC communication class utilizing the CIP (EtherNet/IP) protocol. Prior to usage, the Ethernet module on the PLC side must be configured accordingly. 8 | 9 | #### Header Files 10 | ```c 11 | #include "ab_cip.h" // Provides protocol method interfaces 12 | #include "typedef.h" // Contains some type macro definitions 13 | ``` 14 | 15 | #### Connection Properties 16 | - port: Port number, typically 44818 17 | - plc_type: PLC model, compatible with models such as 1756 ControlLogix, 1756 GuardLogix, 1769 CompactLogix, 1769 Compact GuardLogix, 1789SoftLogix, 5069 CompactLogix, 5069 Compact GuardLogix, Studio 5000 Logix Emulate, etc. 18 | 19 | #### PLC Address Classification 20 | Supports tag-based read/write operations (current implementation is limited and does not fully encompass all protocol features). 21 | 22 | ## Implementation Details 23 | #### 1. Connecting to PLC Devices 24 | 25 | ```c 26 | byte get_plc_slot(); // Gets PLC slot number 27 | void set_plc_slot(byte slot); // Sets the PLC slot number 28 | 29 | bool ab_cip_connect(char* ip_addr, int port, int slot, int* fd); // Connects to a PLC device 30 | bool ab_cip_disconnect(int fd); // Disconnects from the PLC 31 | ``` 32 | 33 | #### 2. Reading Data 34 | 35 | ```c 36 | cip_error_code_e ab_cip_read_bool(int fd, const char* address, bool* val); // Reads a boolean value 37 | cip_error_code_e ab_cip_read_short(int fd, const char* address, short* val); // Reads a short integer 38 | cip_error_code_e ab_cip_read_ushort(int fd, const char* address, ushort* val); // Reads an unsigned short integer 39 | cip_error_code_e ab_cip_read_int32(int fd, const char* address, int32* val); // Reads a 32-bit integer 40 | cip_error_code_e ab_cip_read_uint32(int fd, const char* address, uint32* val); // Reads an unsigned 32-bit integer 41 | cip_error_code_e ab_cip_read_int64(int fd, const char* address, int64* val); // Reads a 64-bit integer 42 | cip_error_code_e ab_cip_read_uint64(int fd, const char* address, uint64* val); // Reads an unsigned 64-bit integer 43 | cip_error_code_e ab_cip_read_float(int fd, const char* address, float* val); // Reads a floating-point value 44 | cip_error_code_e ab_cip_read_double(int fd, const char* address, double* val); // Reads a double-precision floating-point value 45 | cip_error_code_e ab_cip_read_string(int fd, const char* address, int* length, char** val); // Reads a string (requires freeing memory allocated for `val`) 46 | ``` 47 | 48 | ### 3. Writing Data 49 | 50 | ```c 51 | cip_error_code_e ab_cip_write_bool(int fd, const char* address, bool val); // Writes a boolean value 52 | cip_error_code_e ab_cip_write_short(int fd, const char* address, short val); // Writes a short integer 53 | cip_error_code_e ab_cip_write_ushort(int fd, const char* address, ushort val); // Writes an unsigned short integer 54 | cip_error_code_e ab_cip_write_int32(int fd, const char* address, int32 val); // Writes a 32-bit integer 55 | cip_error_code_e ab_cip_write_uint32(int fd, const char* address, uint32 val); // Writes an unsigned 32-bit integer 56 | cip_error_code_e ab_cip_write_int64(int fd, const char* address, int64 val); // Writes a 64-bit integer 57 | cip_error_code_e ab_cip_write_uint64(int fd, const char* address, uint64 val); // Writes an unsigned 64-bit integer 58 | cip_error_code_e ab_cip_write_float(int fd, const char* address, float val); // Writes a floating-point value 59 | cip_error_code_e ab_cip_write_double(int fd, const char* address, double val); // Writes a double-precision floating-point value 60 | // Not supported at the moment, there is a bug 61 | cip_error_code_e ab_cip_write_string(int fd, const char* address, int length, const char* val); // Writes a string 62 | ``` 63 | 64 | ## Usage Example 65 | 66 | Refer to the main.c file in the code for the complete example. Below are the main code segments and usage instructions: 67 | 68 | Reading addresses follows the format "F" or "D". 69 | 70 | ```c 71 | #ifdef _WIN32 72 | #include 73 | #endif 74 | #include 75 | #include 76 | #pragma warning(disable : 4996) 77 | 78 | #define GET_RESULT(ret) { if (ret != 0) failed_count++;} 79 | 80 | #include "ab_cip.h" 81 | 82 | int main(int argc, char** argv) 83 | { 84 | #ifdef _WIN32 85 | WSADATA wsa; 86 | if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) 87 | { 88 | return -1; 89 | } 90 | #endif 91 | 92 | char* plc_ip = "127.0.0.1"; 93 | int plc_port = 44818; 94 | if (argc > 1) 95 | { 96 | plc_ip = argv[1]; 97 | plc_port = atoi(argv[2]); 98 | } 99 | 100 | int fd = -1; 101 | int slot = 0; 102 | bool ret = ab_cip_connect(plc_ip, plc_port, 0, &fd); 103 | if (ret && fd > 0) 104 | { 105 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 106 | 107 | const int TEST_COUNT = 5000; 108 | const int TEST_SLEEP_TIME = 1000; 109 | int failed_count = 0; 110 | char address[50] = { 0 }; 111 | int i = 0; 112 | 113 | for (i = 0; i < TEST_COUNT; i++) 114 | { 115 | printf("==============Test count: %d==============\n", i + 1); 116 | bool all_success = false; 117 | ////////////////////////////////////////////////////////////////////////// 118 | bool val = true; 119 | strcpy(address, "E"); 120 | ret = ab_cip_write_bool(fd, address, val); 121 | printf("Write\t %s \tbool:\t %d, \tret: %d\n", address, val, ret); 122 | GET_RESULT(ret); 123 | 124 | val = false; 125 | ret = ab_cip_read_bool(fd, address, &val); 126 | printf("Read\t %s \tbool:\t %d\n", address, val); 127 | GET_RESULT(ret); 128 | 129 | ////////////////////////////////////////////////////////////////////////// 130 | short w_s_val = 23; 131 | strcpy(address, "A"); 132 | ret = ab_cip_write_short(fd, address, w_s_val); 133 | printf("Write\t %s \tshort:\t %d, \tret: %d\n", address, w_s_val, ret); 134 | GET_RESULT(ret); 135 | 136 | short s_val = 0; 137 | ret = ab_cip_read_short(fd, address, &s_val); 138 | printf("Read\t %s \tshort:\t %d\n", address, s_val); 139 | GET_RESULT(ret); 140 | 141 | ////////////////////////////////////////////////////////////////////////// 142 | ushort w_us_val = 255; 143 | strcpy(address, "A"); 144 | ret = ab_cip_write_ushort(fd, address, w_us_val); 145 | printf("Write\t %s \tushort:\t %d, \tret: %d\n", address, w_us_val, ret); 146 | GET_RESULT(ret); 147 | 148 | ushort us_val = 0; 149 | ret = ab_cip_read_ushort(fd, address, &us_val); 150 | printf("Read\t %s \tushort:\t %d\n", address, us_val); 151 | GET_RESULT(ret); 152 | 153 | ////////////////////////////////////////////////////////////////////////// 154 | int32 w_i_val = 12345; 155 | strcpy(address, "B"); 156 | ret = ab_cip_write_int32(fd, address, w_i_val); 157 | printf("Write\t %s \tint32:\t %d, \tret: %d\n", address, w_i_val, ret); 158 | GET_RESULT(ret); 159 | 160 | int i_val = 0; 161 | ret = ab_cip_read_int32(fd, address, &i_val); 162 | printf("Read\t %s \tint32:\t %d\n", address, i_val); 163 | GET_RESULT(ret); 164 | 165 | ////////////////////////////////////////////////////////////////////////// 166 | uint32 w_ui_val = 22345; 167 | ret = ab_cip_write_uint32(fd, address, w_ui_val); 168 | printf("Write\t %s \tuint32:\t %d, \tret: %d\n", address, w_ui_val, ret); 169 | GET_RESULT(ret); 170 | 171 | uint32 ui_val = 0; 172 | ret = ab_cip_read_uint32(fd, address, &ui_val); 173 | printf("Read\t %s \tuint32:\t %d\n", address, ui_val); 174 | GET_RESULT(ret); 175 | 176 | ////////////////////////////////////////////////////////////////////////// 177 | #if true 178 | int64 w_i64_val = 333334554; 179 | strcpy(address, "N"); 180 | ret = ab_cip_write_int64(fd, address, w_i64_val); 181 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_i64_val, ret); 182 | GET_RESULT(ret); 183 | 184 | int64 i64_val = 0; 185 | ret = ab_cip_read_int64(fd, address, &i64_val); 186 | printf("Read\t %s \tint64:\t %lld\n", address, i64_val); 187 | GET_RESULT(ret); 188 | 189 | #endif 190 | ////////////////////////////////////////////////////////////////////////// 191 | uint64 w_ui64_val = 4333334554; 192 | strcpy(address, "N"); 193 | ret = ab_cip_write_uint64(fd, address, w_ui64_val); 194 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_ui64_val, ret); 195 | GET_RESULT(ret); 196 | 197 | int64 ui64_val = 0; 198 | ret = ab_cip_read_uint64(fd, address, &ui64_val); 199 | printf("Read\t %s \tuint64:\t %lld\n", address, ui64_val); 200 | GET_RESULT(ret); 201 | 202 | ////////////////////////////////////////////////////////////////////////// 203 | float w_f_val = 32.454f; 204 | strcpy(address, "C"); 205 | ret = ab_cip_write_float(fd, address, w_f_val); 206 | printf("Write\t %s \tfloat:\t %f, \tret: %d\n", address, w_f_val, ret); 207 | GET_RESULT(ret); 208 | 209 | float f_val = 0; 210 | ret = ab_cip_read_float(fd, address, &f_val); 211 | printf("Read\t %s \tfloat:\t %f\n", address, f_val); 212 | GET_RESULT(ret); 213 | 214 | ////////////////////////////////////////////////////////////////////////// 215 | #if true 216 | // this function NEED TEST 217 | double w_d_val = 12345.6789; 218 | strcpy(address, "Double"); 219 | ret = ab_cip_write_double(fd, address, w_d_val); 220 | printf("Write\t %s \tdouble:\t %lf, \tret: %d\n", address, w_d_val, ret); 221 | GET_RESULT(ret); 222 | 223 | double d_val = 0; 224 | ret = ab_cip_read_double(fd, address, &d_val); 225 | printf("Read\t %s \tdouble:\t %lf\n", address, d_val); 226 | GET_RESULT(ret); 227 | 228 | #endif 229 | ////////////////////////////////////////////////////////////////////////// 230 | #if true 231 | int length = 0; 232 | strcpy(address, "F"); 233 | char* str_val = NULL; 234 | 235 | ret = ab_cip_read_string(fd, address, &length, &str_val); 236 | printf("Read\t %s \tstring:\t %s\n", address, str_val); 237 | free(str_val); 238 | GET_RESULT(ret); 239 | #endif 240 | 241 | #ifdef _WIN32 242 | Sleep(TEST_SLEEP_TIME); 243 | #else 244 | usleep(TEST_SLEEP_TIME * 1000); 245 | #endif 246 | } 247 | 248 | printf("All Failed count: %d\n", failed_count); 249 | 250 | ab_cip_disconnect(fd); 251 | system("pause"); 252 | } 253 | 254 | #ifdef _WIN32 255 | WSACleanup(); 256 | #endif 257 | } 258 | ``` 259 | -------------------------------------------------------------------------------- /ab_plc_cip_net/ab_cip_helper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ab_cip_helper.h" 5 | #include "socket.h" 6 | 7 | extern uint32 g_session; 8 | extern byte g_plc_slot; 9 | extern byte g_registered_command[28]; 10 | 11 | // 从地址构造核心报文 12 | byte_array_info build_read_core_command(const char* address, int length) 13 | { 14 | size_t addr_length = strlen(address); 15 | size_t addr_adjust_length = addr_length; 16 | if (addr_adjust_length % 2 == 1) 17 | addr_adjust_length += 1; 18 | ushort command_len = 9 + 26 + (ushort)addr_adjust_length + 1 + 24; 19 | byte* command = (byte*)malloc(command_len); 20 | if (command != NULL) 21 | { 22 | memset(command, 0, command_len); 23 | 24 | char* temp_address = (char*)malloc(addr_adjust_length); 25 | if (temp_address != NULL) 26 | { 27 | memset(temp_address, 0, addr_adjust_length); 28 | memcpy(temp_address, address, strlen(address)); 29 | 30 | command[0] = 0x6F; // 命令 31 | command[2] = (byte)((command_len - 24) % 256); 32 | command[3] = (byte)((command_len - 24) / 256); // 长度 33 | 34 | char temp_session[4] = { 0 }; 35 | uint2bytes(g_session, temp_session); 36 | command[4] = temp_session[0]; 37 | command[5] = temp_session[1]; 38 | command[6] = temp_session[2]; 39 | command[7] = temp_session[3]; // 会话句柄 40 | 41 | command[0 + 24] = 0x00; 42 | command[1 + 24] = 0x00; 43 | command[2 + 24] = 0x00; 44 | command[3 + 24] = 0x00; // 接口句柄,默认为0x00000000(CIP) 45 | command[4 + 24] = 0x01; 46 | command[5 + 24] = 0x0A; // 超时(0x000A) 47 | command[6 + 24] = 0x02; 48 | command[7 + 24] = 0x00; // 项数(0x0002) 49 | command[8 + 24] = 0x00; 50 | command[9 + 24] = 0x00; // 空地址项(0x0000) 51 | command[10 + 24] = 0x00; 52 | command[11 + 24] = 0x00; // 长度(0x0000) 53 | command[12 + 24] = 0xB2; // type id 0xB2:UnConnected Data Item 0xB1:Connected Data Item 0xA1:Connect Address Item 54 | command[13 + 24] = 0x00; // 未连接数据项(0x00b2) 55 | command[14 + 24] = (byte)((command_len - 16 - 24) % 256); // 后面数据包的长度,等全部生成后在赋值 56 | command[15 + 24] = (byte)((command_len - 16 - 24) / 256); 57 | command[16 + 24] = 0x52; // 服务类型(0x03请求服务列表,0x52请求标签数据) 58 | command[17 + 24] = 0x02; // 请求路径大小 59 | command[18 + 24] = 0x20; 60 | command[19 + 24] = 0x06; // 请求路径(0x0620) 61 | command[20 + 24] = 0x24; 62 | command[21 + 24] = 0x01; // 请求路径(0x0124) 63 | command[22 + 24] = 0x0A; 64 | command[23 + 24] = 0xF0; 65 | command[24 + 24] = (byte)((6 + addr_adjust_length) % 256); // CIP指令长度 66 | command[25 + 24] = (byte)((6 + addr_adjust_length) / 256); 67 | 68 | command[0 + 24 + 26] = 0x4C; // 读取数据 69 | command[1 + 24 + 26] = (byte)((addr_adjust_length + 2) / 2); 70 | command[2 + 24 + 26] = 0x91; 71 | command[3 + 24 + 26] = (byte)addr_length; 72 | memcpy(command + 4 + 24 + 26, temp_address, addr_adjust_length); 73 | command[4 + 24 + 26 + addr_adjust_length] = (byte)((length) % 256); 74 | command[5 + 24 + 26 + addr_adjust_length] = (byte)((length) / 256); 75 | 76 | command[6 + 24 + 26 + addr_adjust_length] = 0x01; 77 | command[7 + 24 + 26 + addr_adjust_length] = 0x00; 78 | command[8 + 24 + 26 + addr_adjust_length] = 0x01; 79 | command[9 + 24 + 26 + addr_adjust_length] = g_plc_slot; 80 | } 81 | RELEASE_DATA(temp_address); 82 | } 83 | else 84 | command_len = 0; 85 | 86 | byte_array_info ret = { 0 }; 87 | ret.data = command; 88 | ret.length = command_len; 89 | return ret; 90 | } 91 | 92 | byte_array_info build_write_core_command(const char* address, ushort typeCode, int length, byte_array_info value) 93 | { 94 | int val_len = 0; 95 | if (value.data != NULL) 96 | val_len = value.length; 97 | 98 | size_t addr_length = strlen(address); 99 | size_t addr_adjust_length = addr_length; 100 | if (addr_adjust_length % 2 == 1) 101 | addr_adjust_length += 1; 102 | 103 | ushort command_len = 8 + 26 + (ushort)addr_adjust_length + val_len + 4 + 24; 104 | byte* command = (byte*)malloc(command_len); 105 | if (command != NULL) 106 | { 107 | memset(command, 0, command_len); 108 | 109 | char* temp_address = (char*)malloc(addr_adjust_length); 110 | if (temp_address != NULL) 111 | { 112 | memset(temp_address, 0, addr_adjust_length); 113 | memcpy(temp_address, address, strlen(address)); 114 | 115 | command[0] = 0x6F; // 命令 116 | command[2] = (byte)((command_len - 24) % 256); 117 | command[3] = (byte)((command_len - 24) / 256); // 长度 118 | 119 | char temp_session[4] = { 0 }; 120 | uint2bytes(g_session, temp_session); 121 | command[4] = temp_session[0]; 122 | command[5] = temp_session[1]; 123 | command[6] = temp_session[2]; 124 | command[7] = temp_session[3]; // 会话句柄 125 | 126 | command[0 + 24] = 0x00; 127 | command[1 + 24] = 0x00; 128 | command[2 + 24] = 0x00; 129 | command[3 + 24] = 0x00; // 接口句柄,默认为0x00000000(CIP) 130 | command[4 + 24] = 0x01; 131 | command[5 + 24] = 0x0A; // 超时(0x0001) 132 | command[6 + 24] = 0x02; 133 | command[7 + 24] = 0x00; // 项数(0x0002) 134 | command[8 + 24] = 0x00; 135 | command[9 + 24] = 0x00; 136 | command[10 + 24] = 0x00; 137 | command[11 + 24] = 0x00; // 空地址项(0x0000) 138 | command[12 + 24] = 0xB2; 139 | command[13 + 24] = 0x00; // 未连接数据项(0x00b2) 140 | command[14 + 24] = (byte)((command_len - 16 - 24) % 256); // 后面数据包的长度,等全部生成后在赋值 141 | command[15 + 24] = (byte)((command_len - 16 - 24) / 256); 142 | command[16 + 24] = 0x52; // 服务类型(0x03请求服务列表,0x52请求标签数据) 143 | command[17 + 24] = 0x02; // 请求路径大小 144 | command[18 + 24] = 0x20; 145 | command[19 + 24] = 0x06; // 请求路径(0x0620) 146 | command[20 + 24] = 0x24; 147 | command[21 + 24] = 0x01; // 请求路径(0x0124) 148 | command[22 + 24] = 0x0A; 149 | command[23 + 24] = 0xF0; 150 | command[24 + 24] = (byte)((8 + val_len + addr_adjust_length) % 256); // CIP指令长度 151 | command[25 + 24] = (byte)((8 + val_len + addr_adjust_length) / 256); 152 | 153 | command[0 + 26 + 24] = 0x4D; // 写数据 154 | command[1 + 26 + 24] = (byte)((addr_adjust_length + 2) / 2); 155 | command[2 + 26 + 24] = 0x91; 156 | command[3 + 26 + 24] = (byte)addr_length; 157 | memcpy(command + 4 + 26 + 24, temp_address, addr_adjust_length); 158 | command[4 + 26 + 24 + addr_adjust_length] = (byte)(typeCode % 256); 159 | command[5 + 26 + 24 + addr_adjust_length] = (byte)(typeCode) / 256; 160 | command[6 + 26 + 24 + addr_adjust_length] = (byte)(length % 256); // TODO length ?? 161 | command[7 + 26 + 24 + addr_adjust_length] = (byte)(length / 256); 162 | memcpy(command + 8 + 26 + 24 + addr_adjust_length, value.data, value.length); 163 | 164 | command[8 + 26 + 24 + addr_adjust_length + val_len] = 0x01; 165 | command[9 + 26 + 24 + addr_adjust_length + val_len] = 0x00; 166 | command[10 + 26 + 24 + addr_adjust_length + val_len] = 0x01; 167 | command[11 + 26 + 24 + addr_adjust_length + val_len] = g_plc_slot; 168 | } 169 | RELEASE_DATA(temp_address); 170 | } 171 | else 172 | command_len = 0; 173 | 174 | byte_array_info ret = { 0 }; 175 | ret.data = command; 176 | ret.length = command_len; 177 | return ret; 178 | } 179 | 180 | cip_error_code_e cip_analysis_read_byte(byte_array_info response, byte_array_info* ret) 181 | { 182 | cip_error_code_e ret_code = CIP_ERROR_CODE_SUCCESS; 183 | if (response.length == 0) 184 | return CIP_ERROR_CODE_FAILED; 185 | 186 | int temp_length = 0; 187 | int data_length = 0; 188 | if (response.length >= 40) // index 38 is data length[ushort] 189 | { 190 | data_length = bytes2ushort(response.data + 38); 191 | if (data_length > 6) 192 | { 193 | temp_length = data_length - 6; 194 | ret->data = (byte*)malloc(temp_length); 195 | if (ret->data != NULL) 196 | { 197 | memset(ret->data, 0, temp_length); 198 | memcpy(ret->data, response.data + 46, temp_length); 199 | ret->type = bytes2ushort(response.data + 44); 200 | ret->length = temp_length; 201 | } 202 | else 203 | ret_code = CIP_ERROR_CODE_MALLOC_FAILED; 204 | } 205 | } 206 | else 207 | { 208 | ret_code = CIP_ERROR_CODE_UNKOWN; 209 | } 210 | if (ret->data == NULL) { 211 | ret_code = CIP_ERROR_CODE_UNKOWN; 212 | } 213 | return ret_code; 214 | } 215 | 216 | cip_error_code_e cip_analysis_write_byte(byte_array_info response) 217 | { 218 | cip_error_code_e ret_code = CIP_ERROR_CODE_SUCCESS; 219 | if (response.length == 0) 220 | return CIP_ERROR_CODE_FAILED; 221 | 222 | return ret_code; 223 | } 224 | 225 | /// 226 | /// 读取数据 227 | /// 长度(length)默认为:1 228 | /// 229 | /// 230 | /// 231 | /// 232 | /// 233 | /// 234 | cip_error_code_e read_value(int fd, const char* address, int length, byte_array_info* out_bytes) 235 | { 236 | cip_error_code_e ret = CIP_ERROR_CODE_UNKOWN; 237 | byte_array_info core_cmd = build_read_core_command(address, length); 238 | if (core_cmd.data != NULL) 239 | { 240 | int need_send = core_cmd.length; 241 | int real_sends = socket_send_data(fd, core_cmd.data, need_send); 242 | if (real_sends == need_send) 243 | { 244 | byte_array_info response = { 0 }; 245 | if (cip_read_response(fd, &response)) 246 | ret = cip_analysis_read_byte(response, out_bytes); 247 | 248 | RELEASE_DATA(response.data); 249 | } 250 | RELEASE_DATA(core_cmd.data); 251 | } 252 | return ret; 253 | } 254 | 255 | cip_error_code_e write_value(int fd, const char* address, int length, ushort type_code, byte_array_info in_bytes) 256 | { 257 | cip_error_code_e ret = CIP_ERROR_CODE_UNKOWN; 258 | byte_array_info core_cmd = build_write_core_command(address, type_code, length, in_bytes); 259 | if (core_cmd.data != NULL) 260 | { 261 | int need_send = core_cmd.length; 262 | int real_sends = socket_send_data(fd, core_cmd.data, need_send); 263 | if (real_sends == need_send) 264 | { 265 | byte_array_info response = { 0 }; 266 | if (cip_read_response(fd, &response)) 267 | ret = cip_analysis_write_byte(response); 268 | 269 | RELEASE_DATA(response.data); 270 | } 271 | RELEASE_DATA(core_cmd.data); 272 | } 273 | return ret; 274 | } 275 | 276 | bool initialization_on_connect(int fd) 277 | { 278 | bool is_ok = false; 279 | g_session = 0; 280 | 281 | // First handshake -> send regiseter command 282 | byte_array_info temp = { 0 }; 283 | int command_len = sizeof(g_registered_command); 284 | temp.data = (byte*)malloc(command_len); 285 | if (temp.data != NULL) 286 | { 287 | memcpy(temp.data, g_registered_command, command_len); 288 | temp.length = command_len; 289 | is_ok = read_data_from_server(fd, temp, &g_session); 290 | RELEASE_DATA(temp.data); 291 | } 292 | 293 | // Return a successful signal 294 | return is_ok; 295 | } 296 | 297 | bool cip_read_response(int fd, byte_array_info* response) 298 | { 299 | bool is_ok = false; 300 | int nread = 0; 301 | int content_size = 0; 302 | 303 | if (fd < 0) 304 | return -1; 305 | 306 | byte* content = NULL; 307 | byte head[HEAD_SIZE]; 308 | memset(head, 0, HEAD_SIZE); 309 | int recv_size = socket_recv_data_one_loop(fd, head, HEAD_SIZE); 310 | if (recv_size >= HEAD_SIZE) // header size 311 | { 312 | content_size = bytes2ushort(head + 2); 313 | if (content_size > 0) 314 | { 315 | content = (byte*)malloc(content_size); 316 | memset(content, 0, content_size); 317 | } 318 | recv_size = socket_recv_data(fd, content, content_size); 319 | if (recv_size == content_size) 320 | { 321 | response->length = HEAD_SIZE + content_size; 322 | response->data = (byte*)malloc(response->length); 323 | if (response->data != NULL) 324 | { 325 | memset(response->data, 0, response->length); 326 | memcpy(response->data, head, HEAD_SIZE); 327 | memcpy(response->data + HEAD_SIZE, content, content_size); 328 | 329 | is_ok = true; 330 | } 331 | } 332 | 333 | RELEASE_DATA(content); 334 | } 335 | return is_ok; 336 | } 337 | 338 | bool read_data_from_server(int fd, byte_array_info send, int* session) 339 | { 340 | bool is_ok = false; 341 | int need_send = send.length; 342 | int real_sends = socket_send_data(fd, send.data, need_send); 343 | if (real_sends != need_send) 344 | return false; 345 | 346 | byte_array_info response = { 0 }; 347 | if (cip_read_response(fd, &response)) 348 | { 349 | if (response.length > 8) 350 | { 351 | *session = bytes2uint32(response.data + 4); 352 | is_ok = true; 353 | } 354 | 355 | RELEASE_DATA(response.data); 356 | } 357 | return is_ok; 358 | } -------------------------------------------------------------------------------- /ab_plc_cip_net/ab_cip.c: -------------------------------------------------------------------------------- 1 | #include "ab_cip_helper.h" 2 | #include "ab_cip.h" 3 | #include "ab_cip_private.h" 4 | 5 | #include "socket.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #ifdef _WIN32 11 | #include 12 | #include 13 | #include 14 | #pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ 15 | #pragma warning(disable : 4996) 16 | #else 17 | #include 18 | #include 19 | #include 20 | #endif 21 | 22 | int port = 44818; 23 | char ip_address[64] = { 0 }; 24 | 25 | bool ab_cip_connect(char* ip_addr, int port, int slot, int* fd) 26 | { 27 | bool ret = false; 28 | int temp_fd = -1; 29 | g_plc_slot = slot; 30 | 31 | temp_fd = socket_open_tcp_client_socket(ip_addr, port); 32 | *fd = temp_fd; 33 | 34 | if (temp_fd > 0) 35 | ret = initialization_on_connect(temp_fd); 36 | 37 | if (!ret && temp_fd > 0) 38 | { 39 | socket_close_tcp_socket(temp_fd); 40 | *fd = -1; 41 | } 42 | return ret; 43 | } 44 | 45 | bool ab_cip_disconnect(int fd) 46 | { 47 | socket_close_tcp_socket(fd); 48 | return true; 49 | } 50 | 51 | cip_error_code_e ab_cip_read_bool(int fd, const char* address, bool* val) 52 | { 53 | if (fd <= 0 || address == NULL || strlen(address) == 0) 54 | return CIP_ERROR_CODE_INVALID_PARAMETER; 55 | 56 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 57 | byte_array_info read_data; 58 | memset(&read_data, 0, sizeof(read_data)); 59 | ret = read_value(fd, address, 1, &read_data); 60 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length > 0) 61 | { 62 | *val = (bool)read_data.data[0]; 63 | } 64 | RELEASE_DATA(read_data.data); 65 | return ret; 66 | } 67 | 68 | cip_error_code_e ab_cip_read_short(int fd, const char* address, short* val) 69 | { 70 | if (fd <= 0 || address == NULL || strlen(address) == 0) 71 | return CIP_ERROR_CODE_INVALID_PARAMETER; 72 | 73 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 74 | byte_array_info read_data; 75 | memset(&read_data, 0, sizeof(read_data)); 76 | ret = read_value(fd, address, 1, &read_data); 77 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length > 0) 78 | { 79 | *val = bytes2short(read_data.data); 80 | } 81 | RELEASE_DATA(read_data.data); 82 | return ret; 83 | } 84 | 85 | cip_error_code_e ab_cip_read_ushort(int fd, const char* address, ushort* val) 86 | { 87 | if (fd <= 0 || address == NULL || strlen(address) == 0) 88 | return CIP_ERROR_CODE_INVALID_PARAMETER; 89 | 90 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 91 | byte_array_info read_data; 92 | memset(&read_data, 0, sizeof(read_data)); 93 | ret = read_value(fd, address, 1, &read_data); 94 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length >= 2) 95 | { 96 | *val = bytes2ushort(read_data.data); 97 | } 98 | RELEASE_DATA(read_data.data); 99 | return ret; 100 | } 101 | 102 | cip_error_code_e ab_cip_read_int32(int fd, const char* address, int32* val) 103 | { 104 | if (fd <= 0 || address == NULL || strlen(address) == 0) 105 | return CIP_ERROR_CODE_INVALID_PARAMETER; 106 | 107 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 108 | byte_array_info read_data; 109 | memset(&read_data, 0, sizeof(read_data)); 110 | ret = read_value(fd, address, 1, &read_data); 111 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length >= 4) 112 | { 113 | *val = bytes2int32(read_data.data); 114 | } 115 | RELEASE_DATA(read_data.data); 116 | return ret; 117 | } 118 | 119 | cip_error_code_e ab_cip_read_uint32(int fd, const char* address, uint32* val) 120 | { 121 | if (fd <= 0 || address == NULL || strlen(address) == 0) 122 | return CIP_ERROR_CODE_INVALID_PARAMETER; 123 | 124 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 125 | byte_array_info read_data; 126 | memset(&read_data, 0, sizeof(read_data)); 127 | ret = read_value(fd, address, 1, &read_data); 128 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length >= 2) 129 | { 130 | *val = bytes2uint32(read_data.data); 131 | } 132 | RELEASE_DATA(read_data.data); 133 | return ret; 134 | } 135 | 136 | cip_error_code_e ab_cip_read_int64(int fd, const char* address, int64* val) 137 | { 138 | if (fd <= 0 || address == NULL || strlen(address) == 0) 139 | return CIP_ERROR_CODE_INVALID_PARAMETER; 140 | 141 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 142 | byte_array_info read_data; 143 | memset(&read_data, 0, sizeof(read_data)); 144 | ret = read_value(fd, address, 1, &read_data); 145 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length >= 8) 146 | { 147 | *val = bytes2bigInt(read_data.data); 148 | } 149 | RELEASE_DATA(read_data.data); 150 | return ret; 151 | } 152 | 153 | cip_error_code_e ab_cip_read_uint64(int fd, const char* address, uint64* val) 154 | { 155 | if (fd <= 0 || address == NULL || strlen(address) == 0) 156 | return CIP_ERROR_CODE_INVALID_PARAMETER; 157 | 158 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 159 | byte_array_info read_data; 160 | memset(&read_data, 0, sizeof(read_data)); 161 | ret = read_value(fd, address, 1, &read_data); 162 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length >= 8) 163 | { 164 | *val = bytes2ubigInt(read_data.data); 165 | } 166 | RELEASE_DATA(read_data.data); 167 | return ret; 168 | } 169 | 170 | cip_error_code_e ab_cip_read_float(int fd, const char* address, float* val) 171 | { 172 | if (fd <= 0 || address == NULL || strlen(address) == 0) 173 | return CIP_ERROR_CODE_INVALID_PARAMETER; 174 | 175 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 176 | byte_array_info read_data; 177 | memset(&read_data, 0, sizeof(read_data)); 178 | ret = read_value(fd, address, 1, &read_data); 179 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length >= 4) 180 | { 181 | *val = bytes2float(read_data.data); 182 | } 183 | RELEASE_DATA(read_data.data); 184 | return ret; 185 | } 186 | 187 | cip_error_code_e ab_cip_read_double(int fd, const char* address, double* val) 188 | { 189 | if (fd <= 0 || address == NULL || strlen(address) == 0) 190 | return CIP_ERROR_CODE_INVALID_PARAMETER; 191 | 192 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 193 | byte_array_info read_data; 194 | memset(&read_data, 0, sizeof(read_data)); 195 | ret = read_value(fd, address, 1, &read_data); 196 | if (ret == CIP_ERROR_CODE_SUCCESS && read_data.length >= 8) 197 | { 198 | *val = bytes2double(read_data.data); 199 | } 200 | RELEASE_DATA(read_data.data); 201 | return ret; 202 | } 203 | 204 | cip_error_code_e ab_cip_read_string(int fd, const char* address, int* length, char** val) 205 | { 206 | if (length == NULL) 207 | return CIP_ERROR_CODE_INVALID_PARAMETER; 208 | 209 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 210 | byte_array_info read_data; 211 | memset(&read_data, 0, sizeof(read_data)); 212 | ret = read_value(fd, address, 1, &read_data); 213 | if (ret != CIP_ERROR_CODE_SUCCESS) 214 | return ret; 215 | if (read_data.length <= 2) 216 | return CIP_ERROR_CODE_FAILED; 217 | 218 | *length = 0; 219 | if (read_data.length >= 6) 220 | { 221 | uint32 str_length = bytes2uint32(read_data.data + 2); 222 | *length = str_length; 223 | int temp_size = str_length + 1; 224 | char* ret_str = (char*)malloc(temp_size); 225 | if (ret_str != NULL) 226 | { 227 | memset(ret_str, 0, temp_size); 228 | memcpy(ret_str, read_data.data + 6, str_length); 229 | *val = ret_str; 230 | } 231 | else 232 | { 233 | ret = CIP_ERROR_CODE_MALLOC_FAILED; 234 | } 235 | } 236 | RELEASE_DATA(read_data.data); 237 | 238 | return ret; 239 | } 240 | 241 | cip_error_code_e ab_cip_write_bool(int fd, const char* address, bool val) 242 | { 243 | if (fd <= 0 || address == NULL || strlen(address) == 0) 244 | return CIP_ERROR_CODE_INVALID_PARAMETER; 245 | 246 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 247 | int write_len = 2; 248 | byte_array_info write_data; 249 | memset(&write_data, 0, sizeof(write_data)); 250 | write_data.data = (byte*)malloc(write_len); 251 | write_data.length = write_len; 252 | 253 | if (val) 254 | { 255 | write_data.data[0] = 0xFF; 256 | write_data.data[1] = 0xFF; 257 | } 258 | ret = write_value(fd, address, 1, CIP_Type_Bool, write_data); 259 | RELEASE_DATA(write_data.data); 260 | return ret; 261 | } 262 | 263 | cip_error_code_e ab_cip_write_short(int fd, const char* address, short val) 264 | { 265 | if (fd <= 0 || address == NULL || strlen(address) == 0) 266 | return CIP_ERROR_CODE_INVALID_PARAMETER; 267 | 268 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 269 | int write_len = 2; 270 | byte_array_info write_data; 271 | memset(&write_data, 0, sizeof(write_data)); 272 | write_data.data = (byte*)malloc(write_len); 273 | write_data.length = write_len; 274 | 275 | short2bytes(val, write_data.data); 276 | ret = write_value(fd, address, 1, CIP_Type_Word, write_data); 277 | RELEASE_DATA(write_data.data); 278 | return ret; 279 | } 280 | 281 | cip_error_code_e ab_cip_write_ushort(int fd, const char* address, ushort val) 282 | { 283 | if (fd <= 0 || address == NULL || strlen(address) == 0) 284 | return CIP_ERROR_CODE_INVALID_PARAMETER; 285 | 286 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 287 | int write_len = 2; 288 | byte_array_info write_data; 289 | memset(&write_data, 0, sizeof(write_data)); 290 | write_data.data = (byte*)malloc(write_len); 291 | write_data.length = write_len; 292 | 293 | ushort2bytes(val, write_data.data); 294 | ret = write_value(fd, address, 1, CIP_Type_UInt, write_data); 295 | RELEASE_DATA(write_data.data); 296 | return ret; 297 | } 298 | 299 | cip_error_code_e ab_cip_write_int32(int fd, const char* address, int32 val) 300 | { 301 | if (fd <= 0 || address == NULL || strlen(address) == 0) 302 | return CIP_ERROR_CODE_INVALID_PARAMETER; 303 | 304 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 305 | int write_len = 4; 306 | byte_array_info write_data; 307 | memset(&write_data, 0, sizeof(write_data)); 308 | write_data.data = (byte*)malloc(write_len); 309 | write_data.length = write_len; 310 | 311 | int2bytes(val, write_data.data); 312 | ret = write_value(fd, address, 1, CIP_Type_DWord, write_data); 313 | RELEASE_DATA(write_data.data); 314 | return ret; 315 | } 316 | 317 | cip_error_code_e ab_cip_write_uint32(int fd, const char* address, uint32 val) 318 | { 319 | if (fd <= 0 || address == NULL || strlen(address) == 0) 320 | return CIP_ERROR_CODE_INVALID_PARAMETER; 321 | 322 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 323 | int write_len = 4; 324 | byte_array_info write_data; 325 | memset(&write_data, 0, sizeof(write_data)); 326 | write_data.data = (byte*)malloc(write_len); 327 | write_data.length = write_len; 328 | 329 | uint2bytes(val, write_data.data); 330 | ret = write_value(fd, address, 1, CIP_Type_UDint, write_data); 331 | RELEASE_DATA(write_data.data); 332 | return ret; 333 | } 334 | 335 | cip_error_code_e ab_cip_write_int64(int fd, const char* address, int64 val) 336 | { 337 | if (fd <= 0 || address == NULL || strlen(address) == 0) 338 | return CIP_ERROR_CODE_INVALID_PARAMETER; 339 | 340 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 341 | int write_len = 8; 342 | byte_array_info write_data; 343 | memset(&write_data, 0, sizeof(write_data)); 344 | write_data.data = (byte*)malloc(write_len); 345 | write_data.length = write_len; 346 | 347 | bigInt2bytes(val, write_data.data); 348 | ret = write_value(fd, address, 1, CIP_Type_LInt, write_data); 349 | RELEASE_DATA(write_data.data); 350 | return ret; 351 | } 352 | 353 | cip_error_code_e ab_cip_write_uint64(int fd, const char* address, uint64 val) 354 | { 355 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 356 | if (fd > 0 && address != NULL) 357 | { 358 | int write_len = 8; 359 | byte_array_info write_data; 360 | memset(&write_data, 0, sizeof(write_data)); 361 | write_data.data = (byte*)malloc(write_len); 362 | write_data.length = write_len; 363 | 364 | ubigInt2bytes(val, write_data.data); 365 | ret = write_value(fd, address, 1, CIP_Type_ULint, write_data); 366 | RELEASE_DATA(write_data.data); 367 | } 368 | return ret; 369 | } 370 | 371 | cip_error_code_e ab_cip_write_float(int fd, const char* address, float val) 372 | { 373 | if (fd <= 0 || address == NULL || strlen(address) == 0) 374 | return CIP_ERROR_CODE_INVALID_PARAMETER; 375 | 376 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 377 | int write_len = 4; 378 | byte_array_info write_data; 379 | memset(&write_data, 0, sizeof(write_data)); 380 | write_data.data = (byte*)malloc(write_len); 381 | write_data.length = write_len; 382 | 383 | float2bytes(val, write_data.data); 384 | ret = write_value(fd, address, 1, CIP_Type_Real, write_data); 385 | RELEASE_DATA(write_data.data); 386 | return ret; 387 | } 388 | 389 | cip_error_code_e ab_cip_write_double(int fd, const char* address, double val) 390 | { 391 | if (fd <= 0 || address == NULL || strlen(address) == 0) 392 | return CIP_ERROR_CODE_INVALID_PARAMETER; 393 | 394 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 395 | int write_len = 8; 396 | byte_array_info write_data; 397 | memset(&write_data, 0, sizeof(write_data)); 398 | write_data.data = (byte*)malloc(write_len); 399 | write_data.length = write_len; 400 | 401 | double2bytes(val, write_data.data); 402 | ret = write_value(fd, address, 1, CIP_Type_Double, write_data); 403 | RELEASE_DATA(write_data.data); 404 | return ret; 405 | } 406 | 407 | cip_error_code_e ab_cip_write_string(int fd, const char* address, int length, const char* val) 408 | { 409 | // this function use Type Code: CIP_Type_Byte 410 | // NOT SUPPORT Type Code 0xDA 411 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 412 | return CIP_ERROR_CODE_INVALID_PARAMETER; 413 | 414 | cip_error_code_e ret = CIP_ERROR_CODE_FAILED; 415 | // 1. write length is even 416 | byte write_len = length; 417 | if (write_len % 2 == 1) 418 | write_len += 1; 419 | 420 | byte_array_info write_data = { 0 }; 421 | write_data.data = (byte*)malloc(write_len); 422 | if (write_data.data != NULL) 423 | { 424 | memset(write_data.data, 0, write_len); 425 | memcpy(write_data.data, val, length); 426 | write_data.length = write_len; 427 | 428 | // 2. write length 429 | char temp_addr[100] = { 0 }; 430 | sprintf(temp_addr, "%s.LEN", address); 431 | ret = ab_cip_write_int32(fd, temp_addr, length); 432 | 433 | // 3. write data 434 | if (ret == CIP_ERROR_CODE_SUCCESS) 435 | { 436 | memset(temp_addr, 0, 100); 437 | sprintf(temp_addr, "%s.DATA[0]", address); 438 | ret = write_value(fd, temp_addr, 1, CIP_Type_Byte, write_data); 439 | } 440 | } 441 | else 442 | { 443 | ret = CIP_ERROR_CODE_MALLOC_FAILED; 444 | } 445 | RELEASE_DATA(write_data.data); 446 | return ret; 447 | } 448 | 449 | byte get_plc_slot() 450 | { 451 | return g_plc_slot; 452 | } 453 | 454 | void set_plc_slot(byte slot) 455 | { 456 | g_plc_slot = slot; 457 | } --------------------------------------------------------------------------------