├── 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 | }
--------------------------------------------------------------------------------