├── s7_plc_test ├── siemens_plc_s7_net ├── makefile ├── socket.h ├── siemens_s7_comm.h ├── siemens_helper.h ├── simens_plc_s7_net.sln ├── utill.h ├── typedef.h ├── simens_plc_s7_net.vcxproj.filters ├── siemens_s7.h ├── socket.c ├── siemens_s7_private.h ├── siemens_s7_comm.c ├── dynstr.h ├── main.c ├── utill.c ├── simens_plc_s7_net.vcxproj ├── siemens_helper.c ├── siemens_s7.c └── dynstr.c ├── config.mk ├── makefile ├── LICENSE ├── .gitattributes ├── common.mk ├── .gitignore ├── README.md └── README_EN.md /s7_plc_test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-pms/siemens_plc_s7_net/HEAD/s7_plc_test -------------------------------------------------------------------------------- /siemens_plc_s7_net/makefile: -------------------------------------------------------------------------------- 1 | 2 | #只生成 .d,.o即可 3 | BIN = s7_plc_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)/siemens_plc_s7_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 | -------------------------------------------------------------------------------- /siemens_plc_s7_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 | -------------------------------------------------------------------------------- /siemens_plc_s7_net/siemens_s7_comm.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_SIEMENS_S7_COMM_H__ 2 | #define __H_SIEMENS_S7_COMM_H__ 3 | #include "utill.h" 4 | 5 | #define MAX_RETRY_TIMES 3 // 最大重试次数 6 | #define MIN_HEADER_SIZE 21 // 根据协议定义 7 | #define BUFFER_SIZE 1024 8 | 9 | typedef struct _tag_siemens_s7_address_data { 10 | byte data_code; // 类型的代号值 11 | ushort db_block; // 获取或设置PLC的DB块数据信息 12 | int address_start; // 数字的起始地址,也就是偏移地址 13 | int length; // 读取的数据长度 14 | }siemens_s7_address_data; 15 | 16 | bool s7_analysis_address(const char* address, int length, siemens_s7_address_data* address_data); 17 | 18 | #endif//__H_SIEMENS_S7_COMM_H__ -------------------------------------------------------------------------------- /siemens_plc_s7_net/siemens_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_SIEMENS_HELPER_H__ 2 | #define __H_SIEMENS_HELPER_H__ 3 | #include "siemens_s7_comm.h" 4 | 5 | byte_array_info build_read_byte_command(siemens_s7_address_data address); 6 | byte_array_info build_read_bit_command(siemens_s7_address_data address); 7 | byte_array_info build_write_byte_command(siemens_s7_address_data address, byte_array_info value); 8 | byte_array_info build_write_bit_command(siemens_s7_address_data address, bool value); 9 | 10 | s7_error_code_e s7_analysis_read_bit(byte_array_info resposne, byte_array_info* ret); 11 | s7_error_code_e s7_analysis_read_byte(byte_array_info response, byte_array_info* ret); 12 | s7_error_code_e s7_analysis_write(byte_array_info response); 13 | 14 | bool read_data_from_core_server(int fd, byte_array_info send, byte_array_info* ret); 15 | bool send_data_to_core_server(int fd, byte_array_info send); 16 | bool try_send_data_to_server(int fd, byte_array_info* in_bytes, int* real_sends); 17 | 18 | #endif//__H_SIEMENS_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 | -------------------------------------------------------------------------------- /siemens_plc_s7_net/simens_plc_s7_net.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.902 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simens_plc_s7_net", "simens_plc_s7_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 = {E0FB60F9-5317-40F8-B73D-9A900FBB572C} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /siemens_plc_s7_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 | byte* data; // 内容 10 | int length; // 长度 11 | }byte_array_info; 12 | 13 | typedef struct _tag_bool_array_info { 14 | bool* data; // 内容 15 | int length; // 长度 16 | }bool_array_info; 17 | 18 | void short2bytes(short i, byte* bytes); 19 | short bytes2short(byte* bytes); 20 | 21 | void ushort2bytes(ushort i, byte* bytes); 22 | ushort bytes2ushort(byte* bytes); 23 | 24 | void int2bytes(int32 i, byte* bytes); 25 | int32 bytes2int32(byte* bytes); 26 | 27 | void uint2bytes(uint32 i, byte* bytes); 28 | uint32 bytes2uint32(byte* bytes); 29 | 30 | void bigInt2bytes(int64 i, byte* bytes); 31 | int64 bytes2bigInt(byte* bytes); 32 | 33 | void ubigInt2bytes(uint64 i, byte* bytes); 34 | uint64 bytes2ubigInt(byte* bytes); 35 | 36 | void float2bytes(float i, byte* bytes); 37 | float bytes2float(byte* bytes); 38 | 39 | void double2bytes(double i, byte* bytes); 40 | double bytes2double(byte* bytes); 41 | 42 | int str_to_int(const char* address); 43 | void str_toupper(char* input); 44 | void str_tolower(char* input); 45 | int str_start_with(const char* origin, char* prefix); 46 | 47 | uint32 htonf_(float value); 48 | float ntohf_(uint32 value); 49 | uint64 htond_(double value); 50 | double ntohd_(uint64 value); 51 | uint64 htonll_(uint64 Value); 52 | uint64 ntohll_(uint64 Value); 53 | 54 | #ifndef _WIN32 55 | char* itoa(unsigned long long value, char str[], int radix); 56 | #endif // !_WIN32 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /siemens_plc_s7_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_siemens_plc_type { 15 | S1200 = 1, // 1200系列 16 | S300 = 2, // 300系列 17 | S400 = 3, // 400系列 18 | S1500 = 4, // 1500系列PLC 19 | S200Smart = 5, // 200的smart系列 20 | S200 = 6 // 200系统,需要额外配置以太网模块 21 | } siemens_plc_types_e; 22 | 23 | typedef enum _tag_s7_error_code { 24 | S7_ERROR_CODE_SUCCESS = 0, // 成功 25 | S7_ERROR_CODE_FAILED = 1, // 错误 26 | S7_ERROR_CODE_FW_ERROR, // 发生了异常,具体信息查找Fetch/Write协议文档 27 | S7_ERROR_CODE_ERROR_0006, // 当前操作的数据类型不支持 28 | S7_ERROR_CODE_ERROR_000A, // 尝试读取不存在的DB块数据 29 | S7_ERROR_CODE_WRITE_ERROR, // 写入数据异常 30 | S7_ERROR_CODE_DB_SIZE_TOO_LARGE, // DB块数据无法大于255 31 | S7_ERROR_CODE_READ_LENGTH_MAST_BE_EVEN, // 读取的数据长度必须为偶数 32 | S7_ERROR_CODE_DATA_LENGTH_CHECK_FAILED, // 数据块长度校验失败,请检查是否开启put/get以及关闭db块优化 33 | S7_ERROR_CODE_READ_LENGTH_OVER_PLC_ASSIGN, // 读取的数据范围超出了PLC的设定 34 | S7_ERROR_CODE_READ_LENGTH_CANNT_LARAGE_THAN_19, // 读取的数组数量不允许大于19 35 | S7_ERROR_CODE_MALLOC_FAILED, // 内存分配错误 36 | S7_ERROR_CODE_INVALID_PARAMETER, // 参数错误 37 | S7_ERROR_CODE_PARSE_ADDRESS_FAILED, // 地址解析错误 38 | S7_ERROR_CODE_BUILD_CORE_CMD_FAILED, // 构建核心命令错误 39 | S7_ERROR_CODE_SOCKET_SEND_FAILED, // 发送命令错误 40 | S7_ERROR_CODE_RESPONSE_HEADER_FAILED, // 响应包头不完整错误 41 | S7_ERROR_CODE_UNKOWN = 99, // 未知错误 42 | } s7_error_code_e; 43 | 44 | #endif // !__H_TYPEDEF_H__ -------------------------------------------------------------------------------- /siemens_plc_s7_net/simens_plc_s7_net.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src 7 | 8 | 9 | src 10 | 11 | 12 | src 13 | 14 | 15 | src 16 | 17 | 18 | src 19 | 20 | 21 | src 22 | 23 | 24 | 25 | 26 | include 27 | 28 | 29 | include 30 | 31 | 32 | src 33 | 34 | 35 | src 36 | 37 | 38 | src 39 | 40 | 41 | src 42 | 43 | 44 | src 45 | 46 | 47 | src 48 | 49 | 50 | 51 | 52 | {6ac6160e-7b52-4c39-8f5e-28afecc35d25} 53 | 54 | 55 | {674fecc8-c748-4058-a191-d4db34e4e56e} 56 | 57 | 58 | -------------------------------------------------------------------------------- /siemens_plc_s7_net/siemens_s7.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_SIEMENS_S7_H__ 2 | #define __H_SIEMENS_S7_H__ 3 | 4 | #include "typedef.h" 5 | 6 | ///////////////////////////////////////////////////////////// 7 | 8 | byte get_plc_slot(); 9 | void set_plc_slot(byte slot); 10 | 11 | byte get_plc_rack(); 12 | void set_plc_rack(byte rack); 13 | 14 | byte get_plc_connection_type(); 15 | void set_plc_connection_type(byte type); 16 | 17 | int get_plc_local_TSAP(); 18 | void set_plc_local_TSAP(int tasp); 19 | 20 | int get_plc_dest_TSAP(); 21 | void set_plc_dest_TSAP(int tasp); 22 | 23 | int get_plc_PDU_length(); 24 | 25 | ///////////////////////////////////////////////////////////// 26 | 27 | bool s7_connect(char* ip_addr, int port, siemens_plc_types_e plc, int* fd); 28 | bool s7_disconnect(int fd); 29 | 30 | //read 31 | s7_error_code_e s7_read_bool(int fd, const char* address, bool* val); 32 | s7_error_code_e s7_read_byte(int fd, const char* address, byte* val); 33 | s7_error_code_e s7_read_short(int fd, const char* address, short* val); 34 | s7_error_code_e s7_read_ushort(int fd, const char* address, ushort* val); 35 | s7_error_code_e s7_read_int32(int fd, const char* address, int32* val); 36 | s7_error_code_e s7_read_uint32(int fd, const char* address, uint32* val); 37 | s7_error_code_e s7_read_int64(int fd, const char* address, int64* val); 38 | s7_error_code_e s7_read_uint64(int fd, const char* address, uint64* val); 39 | s7_error_code_e s7_read_float(int fd, const char* address, float* val); 40 | s7_error_code_e s7_read_double(int fd, const char* address, double* val); 41 | s7_error_code_e s7_read_string(int fd, const char* address, int length, char** val); //need free val 42 | 43 | //write 44 | s7_error_code_e s7_write_bool(int fd, const char* address, bool val); 45 | s7_error_code_e s7_write_byte(int fd, const char* address, byte val); 46 | s7_error_code_e s7_write_short(int fd, const char* address, short val); 47 | s7_error_code_e s7_write_ushort(int fd, const char* address, ushort val); 48 | s7_error_code_e s7_write_int32(int fd, const char* address, int32 val); 49 | s7_error_code_e s7_write_uint32(int fd, const char* address, uint32 val); 50 | s7_error_code_e s7_write_int64(int fd, const char* address, int64 val); 51 | s7_error_code_e s7_write_uint64(int fd, const char* address, uint64 val); 52 | s7_error_code_e s7_write_float(int fd, const char* address, float val); 53 | s7_error_code_e s7_write_double(int fd, const char* address, double val); 54 | s7_error_code_e s7_write_string(int fd, const char* address, int length, const char* val); 55 | 56 | // 57 | s7_error_code_e s7_remote_run(int fd); 58 | s7_error_code_e s7_remote_stop(int fd); 59 | s7_error_code_e s7_remote_reset(int fd); 60 | s7_error_code_e s7_read_plc_type(int fd, char** type); 61 | 62 | #endif //__H_SIEMENS_S7_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 | -------------------------------------------------------------------------------- /siemens_plc_s7_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 | int nleft, nwritten; 20 | char* ptr = (char*)buf; 21 | 22 | if (fd < 0) return -1; 23 | 24 | nleft = nbytes; 25 | while (nleft > 0) { 26 | nwritten = send(fd, ptr, nleft, 0); 27 | if (nwritten <= 0) { 28 | if (errno == EINTR) 29 | continue; 30 | else 31 | return -1; 32 | } 33 | else { 34 | nleft -= nwritten; 35 | ptr += nwritten; 36 | } 37 | } 38 | 39 | return (nbytes - nleft); 40 | } 41 | 42 | int socket_recv_data(int fd, void* buf, int nbytes) { 43 | int nleft, nread; 44 | char* ptr = (char*)buf; 45 | 46 | if (fd < 0) return -1; 47 | 48 | nleft = nbytes; 49 | while (nleft > 0) { 50 | nread = recv(fd, ptr, nleft, 0); 51 | if (nread == 0) { 52 | break; 53 | } 54 | else if (nread < 0) { 55 | if (errno == EINTR) 56 | continue; 57 | else 58 | return -1; 59 | } 60 | else { 61 | nleft -= nread; 62 | ptr += nread; 63 | } 64 | } 65 | 66 | return (nbytes - nleft); 67 | } 68 | 69 | int socket_recv_data_one_loop(int fd, void* buf, int nbytes) { 70 | int nleft, nread; 71 | char* ptr = (char*)buf; 72 | 73 | if (fd < 0) return -1; 74 | 75 | nleft = nbytes; 76 | while (nleft > 0) { 77 | nread = recv(fd, ptr, nleft, 0); 78 | if (nread == 0) { 79 | break; 80 | } 81 | else if (nread < 0) { 82 | if (errno == EINTR) 83 | continue; 84 | else 85 | return -1; 86 | } 87 | else { 88 | nleft -= nread; 89 | ptr += nread; 90 | 91 | // 目前只接收一次 92 | break; 93 | } 94 | } 95 | 96 | return (nbytes - nleft); 97 | } 98 | 99 | int socket_open_tcp_client_socket(char* destIp, short destPort) { 100 | int sockFd = 0; 101 | struct sockaddr_in serverAddr; 102 | int ret; 103 | 104 | sockFd = (int)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 105 | 106 | if (sockFd < 0) { 107 | return -1; 108 | #pragma warning(disable:4996) 109 | } 110 | 111 | memset((char*)&serverAddr, 0, sizeof(serverAddr)); 112 | serverAddr.sin_family = AF_INET; 113 | serverAddr.sin_addr.s_addr = inet_addr(destIp); 114 | serverAddr.sin_port = (uint16_t)htons((uint16_t)destPort); 115 | 116 | ret = connect(sockFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); 117 | 118 | if (ret != 0) { 119 | socket_close_tcp_socket(sockFd); 120 | sockFd = -1; 121 | } 122 | 123 | #ifdef _WIN32 124 | int timeout = 5000; //5s 125 | ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); 126 | ret = setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); 127 | #else 128 | struct timeval timeout = { 5,0 };//3s 129 | ret = setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); 130 | ret = setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); 131 | #endif 132 | 133 | return sockFd; 134 | } 135 | 136 | void socket_close_tcp_socket(int sockFd) { 137 | if (sockFd > 0) 138 | { 139 | #ifdef _WIN32 140 | closesocket(sockFd); 141 | #else 142 | close(sockFd); 143 | #endif 144 | } 145 | } 146 | 147 | void tinet_ntoa(char* ipstr, unsigned int ip) { 148 | sprintf(ipstr, "%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24); 149 | } -------------------------------------------------------------------------------- /siemens_plc_s7_net/siemens_s7_private.h: -------------------------------------------------------------------------------- 1 | #ifndef __H_SIEMENS_S7_PRIVATE_H__ 2 | #define __H_SIEMENS_S7_PRIVATE_H__ 3 | 4 | #include "siemens_s7_comm.h" 5 | 6 | //private byte[] plcHead1 = "03 00 00 16 11 E0 00 00 00 02 00 C1 02 01 00 C2 02 01 02 C0 01 0A".ToHexBytes( ); 7 | //private byte[] plcHead2 = "03 00 00 19 02 F0 80 32 01 00 00 02 00 00 08 00 00 F0 00 00 01 00 01 01 E0".ToHexBytes( ); 8 | byte g_plc_head1[] = 9 | { 10 | 0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01, 11 | 0x00,0xC0,0x01,0x0A,0xC1,0x02,0x01,0x02,0xC2,0x02, 12 | 0x01,0x00 13 | }; 14 | byte g_plc_head2[] = 15 | { 16 | 0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00, 17 | 0x00,0x04,0x00,0x00,0x08,0x00,0x00,0xF0,0x00,0x00, 18 | 0x01,0x00,0x01,0x01,0xE0 19 | }; 20 | byte g_plc_order_number[] = 21 | { 22 | 0x03,0x00,0x00,0x21,0x02,0xF0,0x80,0x32,0x07,0x00, 23 | 0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x01,0x12, 24 | 0x04,0x11,0x44,0x01,0x00,0xFF,0x09,0x00,0x04,0x00, 25 | 0x11,0x00,0x00 26 | }; 27 | 28 | byte g_plc_head1_200smart[22] = 29 | { 30 | 0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01, 31 | 0x00,0xC1,0x02,0x10,0x00,0xC2,0x02,0x03,0x00,0xC0, 32 | 0x01,0x0A 33 | }; 34 | 35 | byte g_plc_head2_200smart[25] = 36 | { 37 | 0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00, 38 | 0x00,0xCC,0xC1,0x00,0x08,0x00,0x00,0xF0,0x00, 39 | 0x00,0x01,0x00,0x01,0x03,0xC0 40 | }; 41 | 42 | byte g_plc_head1_200[22] = 43 | { 44 | 0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,0x00,0x01, 45 | 0x00,0xC1,0x02,0x4D,0x57,0xC2,0x02,0x4D,0x57,0xC0, 46 | 0x01,0x09 47 | }; 48 | 49 | byte g_plc_head2_200[25] = 50 | { 51 | 0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,0x01,0x00, 52 | 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0xF0,0x00,0x00, 53 | 0x01,0x00,0x01,0x03,0xC0 54 | }; 55 | 56 | const byte g_s7_stop[] = { 57 | 0x03, 0x00, 0x00, 0x21, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 58 | 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x00, 0x29, 0x00, 0x00, 59 | 0x00, 0x00, 0x00, 0x09, 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x47, 60 | 0x52, 0x41, 0x4d 61 | }; 62 | 63 | const byte g_s7_hot_start[] = { 64 | 0x03, 0x00, 0x00, 0x25, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 65 | 0x00, 0x0c, 0x00, 0x00, 0x14, 0x00, 0x00, 0x28, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x09, 0x50, 0x5f, 67 | 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d 68 | }; 69 | 70 | const byte g_s7_cold_start[] = { 71 | 0x03, 0x00, 0x00, 0x27, 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 72 | 0x00, 0x0f, 0x00, 0x00, 0x16, 0x00, 0x00, 0x28, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x02, 0x43, 0x20, 0x09, 74 | 0x50, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d 75 | }; 76 | 77 | /////////////////////////////////////////////////////////////////////////// 78 | const byte g_pdu_start = 0x28; // CPU start 79 | const byte g_pdu_stop = 0x29; // CPU stop 80 | const byte g_pdu_already_started = 0x02; // CPU already in run mode 81 | const byte g_pdu_already_stopped = 0x07; // CPU already in stop mode 82 | 83 | byte g_plc_rack = 0x00; 84 | byte g_plc_slot = 0x00; 85 | int g_pdu_length = 0; 86 | 87 | s7_error_code_e s7_read_response(int fd, byte_array_info* response, int* read_count); 88 | 89 | void s7_initialization(siemens_plc_types_e plc, char* ip); 90 | 91 | ////////////////////////////////////////////////////////////////////////// 92 | s7_error_code_e read_bit_value(int fd, const char* address, int length, byte_array_info* out_bytes); 93 | s7_error_code_e read_byte_value(int fd, const char* address, int length, byte_array_info* out_bytes); 94 | s7_error_code_e read_address_data(int fd, siemens_s7_address_data address_data, byte_array_info* out_bytes); 95 | 96 | s7_error_code_e write_bit_value(int fd, const char* address, int length, bool value); 97 | s7_error_code_e write_byte_value(int fd, const char* address, int length, byte_array_info in_bytes); 98 | s7_error_code_e write_address_data(int fd, siemens_s7_address_data address_data, byte_array_info in_bytes); 99 | 100 | ////////////////////////////////////////////////////////////////////////// 101 | bool initialization_on_connect(int fd); 102 | 103 | #endif//__H_SIEMENS_S7_PRIVATE_H__ 104 | -------------------------------------------------------------------------------- /siemens_plc_s7_net/siemens_s7_comm.c: -------------------------------------------------------------------------------- 1 | #include "siemens_s7_comm.h" 2 | #include 3 | #include 4 | #include "dynstr.h" 5 | 6 | /// 7 | /// 计算特殊的地址信息 8 | /// 9 | /// 字符串地址 10 | /// 是否是定时器和计数器的地址 11 | /// 12 | int calculate_address_started(const char* address, bool isCT) 13 | { 14 | int addr_len = strlen(address); 15 | int ret_count = 0; 16 | 17 | int ret = dynstr_find(address, addr_len, ".", 1); 18 | if (ret < 0) // 未找到 -1 19 | { 20 | ret_count = isCT ? str_to_int(address) : str_to_int(address) * 8; 21 | } 22 | else 23 | { 24 | int temp_split_count = 0; 25 | dynstr* ret_splits = dynstr_split(address, addr_len, ".", 1, &temp_split_count); 26 | ret_count = str_to_int(ret_splits[0]) * 8 + str_to_int(ret_splits[1]); 27 | 28 | dynstr_freesplitres(ret_splits, temp_split_count); 29 | } 30 | return ret_count; 31 | } 32 | 33 | bool s7_analysis_address(const char* address, int length, siemens_s7_address_data* address_data) 34 | { 35 | if (address == NULL || length <= 0 || address_data == NULL) 36 | return false; 37 | 38 | address_data->length = length; 39 | address_data->db_block = 0; 40 | 41 | dynstr temp_address = dynstr_new(address); 42 | str_toupper(temp_address); 43 | 44 | const char* prefixes[] = { "AI", "AQ", "I", "Q", "M", "D", "DB", "T", "C", "V" }; 45 | const int data_codes[] = { 0x06, 0x07, 0x81, 0x82, 0x83, 0x84, 0x84, 0x1F, 0x1E, 0x84 }; 46 | const int sub_str_lens[] = { 2, 2, 1, 1, 1, 1, 2, 1, 1, 1 }; 47 | const int prefix_count = sizeof(prefixes) / sizeof(prefixes[0]); 48 | 49 | for (int i = 0; i < prefix_count; ++i) { 50 | if (0 == str_start_with(temp_address, prefixes[i])) { 51 | address_data->data_code = data_codes[i]; 52 | int sub_str_len = sub_str_lens[i]; 53 | 54 | if (i == 5 || i == 6) { // Handle "D" and "DB" separately 55 | int temp_split_count = 0; 56 | dynstr* ret_splits = dynstr_split(temp_address, strlen(temp_address), ".", 1, &temp_split_count); 57 | if (0 == str_start_with(ret_splits[0], "DB")) 58 | sub_str_len = 2; 59 | 60 | dynstr_range(ret_splits[0], sub_str_len, -1); 61 | address_data->db_block = str_to_int(ret_splits[0]); 62 | 63 | if (temp_split_count > 1) { 64 | sub_str_len = 0; 65 | if (0 == str_start_with(ret_splits[1], "DBX") || 66 | 0 == str_start_with(ret_splits[1], "DBB") || 67 | 0 == str_start_with(ret_splits[1], "DBW") || 68 | 0 == str_start_with(ret_splits[1], "DBD")) { 69 | sub_str_len = 3; 70 | } 71 | 72 | dynstr temp_addr = dynstr_dup(ret_splits[1]); 73 | if (temp_split_count > 2) { 74 | dynstr temp_addr2 = dynstr_cat(ret_splits[1], "."); 75 | temp_addr = dynstr_cat_dynstr(temp_addr2, ret_splits[2]); 76 | dynstr_free(temp_addr2); 77 | } 78 | 79 | dynstr_range(temp_addr, sub_str_len, -1); 80 | address_data->address_start = calculate_address_started(temp_addr, false); 81 | dynstr_free(temp_addr); 82 | 83 | dynstr_freesplitres(ret_splits, temp_split_count); 84 | } 85 | } 86 | else { 87 | if (0 == str_start_with(temp_address, "AIX") || 88 | 0 == str_start_with(temp_address, "AIB") || 89 | 0 == str_start_with(temp_address, "AIW") || 90 | 0 == str_start_with(temp_address, "AID") || 91 | 0 == str_start_with(temp_address, "AQX") || 92 | 0 == str_start_with(temp_address, "AQB") || 93 | 0 == str_start_with(temp_address, "AQW") || 94 | 0 == str_start_with(temp_address, "AQD") || 95 | 0 == str_start_with(temp_address, "IX") || 96 | 0 == str_start_with(temp_address, "IB") || 97 | 0 == str_start_with(temp_address, "IW") || 98 | 0 == str_start_with(temp_address, "ID") || 99 | 0 == str_start_with(temp_address, "QX") || 100 | 0 == str_start_with(temp_address, "QB") || 101 | 0 == str_start_with(temp_address, "QW") || 102 | 0 == str_start_with(temp_address, "QD") || 103 | 0 == str_start_with(temp_address, "MX") || 104 | 0 == str_start_with(temp_address, "MB") || 105 | 0 == str_start_with(temp_address, "MW") || 106 | 0 == str_start_with(temp_address, "MD") || 107 | 0 == str_start_with(temp_address, "VX") || 108 | 0 == str_start_with(temp_address, "VB") || 109 | 0 == str_start_with(temp_address, "VW") || 110 | 0 == str_start_with(temp_address, "VD")) { 111 | sub_str_len++; 112 | } 113 | 114 | dynstr_range(temp_address, sub_str_len, -1); 115 | address_data->address_start = calculate_address_started(temp_address, i == 7 || i == 8); 116 | } 117 | break; 118 | } 119 | } 120 | 121 | dynstr_free(temp_address); 122 | return true; 123 | } -------------------------------------------------------------------------------- /siemens_plc_s7_net/dynstr.h: -------------------------------------------------------------------------------- 1 | /* SDS (Simple Dynamic Strings), A C dynamic strings library. 2 | * 3 | * Copyright (c) 2006-2014, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /* 32 | * modify: 33 | * - rename `sds' to `dynstr' 34 | * - add some split function 35 | **/ 36 | 37 | #ifndef __DYNSTR_H__ 38 | #define __DYNSTR_H__ 39 | 40 | #include 41 | #include 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | typedef char* dynstr; 48 | 49 | struct dynstr_hdr { 50 | int len; 51 | int free; 52 | char buf[]; 53 | }; 54 | 55 | dynstr dynstr_newlen(const void* init, size_t initlen); 56 | dynstr dynstr_new(const char* init); 57 | dynstr dynstr_empty(void); 58 | size_t dynstr_len(const dynstr s); 59 | dynstr dynstr_dup(const dynstr s); 60 | void dynstr_free(dynstr s); 61 | size_t dynstr_avail(const dynstr s); 62 | dynstr dynstr_growzero(dynstr s, size_t len); 63 | dynstr dynstr_catlen(dynstr s, const void* t, size_t len); 64 | dynstr dynstr_cat(dynstr s, const char* t); 65 | dynstr dynstr_cat_dynstr(dynstr s, const dynstr t); 66 | dynstr dynstr_cpylen(dynstr s, const char* t, size_t len); 67 | dynstr dynstr_cpy(dynstr s, const char* t); 68 | 69 | dynstr dynstr_catvprintf(dynstr s, const char* fmt, va_list ap); 70 | dynstr dynstr_vsprintf(const char* fmt, va_list ap); 71 | #ifdef __GNUC__ 72 | dynstr dynstr_catprintf(dynstr s, const char* fmt, ...); 73 | // __attribute__((format(printf, 2, 3))); 74 | dynstr dynstr_sprintf(const char* fmt, ...); 75 | // __attribute__((format(printf, 2, 3))); 76 | #else 77 | dynstr dynstr_catprintf(dynstr s, const char* fmt, ...); 78 | dynstr dynstr_sprintf(const char* fmt, ...); 79 | #endif 80 | 81 | void dynstr_trim(dynstr s, const char* cset); 82 | void dynstr_range(dynstr s, int start, int end); 83 | void dynstr_updatelen(dynstr s); 84 | void dynstr_clear(dynstr s); 85 | int dynstr_cmp(const dynstr s1, const dynstr s2); 86 | int dynstr_find(const char* s, int len, const char* p, int m); 87 | int dynstr_rfind(const char* s, int len, const char* p, int m); 88 | dynstr* dynstr_splitwhitespace(const char* s, int len, int* count); 89 | dynstr* dynstr_rsplitwhitespace(const char* s, int len, int* count); 90 | dynstr* dynstr_splitchar(const char* s, int len, const char ch, int* count); 91 | dynstr* dynstr_rsplitchar(const char* s, int len, const char ch, int* count); 92 | dynstr* dynstr_split(const char* s, int len, const char* sep, int seplen, int* count); 93 | dynstr* dynstr_rsplit(const char* s, int len, const char* sep, int seplen, int* count); 94 | dynstr* dynstr_splitlines(const char* s, int len, int* count, int keepends); 95 | void dynstr_freesplitres(dynstr* tokens, int count); 96 | void dynstr_tolower(dynstr s); 97 | void dynstr_toupper(dynstr s); 98 | dynstr dynstr_fromlonglong(long long value); 99 | dynstr dynstr_catrepr(dynstr s, const char* p, size_t len); 100 | dynstr* dynstr_splitargs(const char* line, int* argc); 101 | dynstr dynstr_mapchars(dynstr s, const char* from, const char* to, size_t setlen); 102 | dynstr dynstr_join(char** argv, int argc, char* sep, size_t seplen); 103 | dynstr dynstr_join_dynstr(dynstr* argv, int argc, const char* sep, size_t seplen); 104 | 105 | /* base64 encode and decode */ 106 | dynstr b64encode_standard(const unsigned char* input, size_t len); 107 | dynstr b64decode_standard(const char* input, size_t len); 108 | 109 | /* Low level functions exposed to the user API */ 110 | dynstr dynstrMakeRoomFor(dynstr s, size_t addlen); 111 | void dynstrIncrLen(dynstr s, int incr); 112 | dynstr dynstrRemoveFreeSpace(dynstr s); 113 | size_t dynstrAllocSize(dynstr s); 114 | 115 | #ifdef __cplusplus 116 | } 117 | #endif 118 | #endif /* !__DYNSTR_H__ */ 119 | -------------------------------------------------------------------------------- /siemens_plc_s7_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) faild_count++; } 9 | #define RELEASE_DATA(data) do { if ((data) != NULL) { free(data); (data) = NULL; } } while(0) 10 | 11 | #include "siemens_s7.h" 12 | 13 | int main(int argc, char** argv) 14 | { 15 | #ifdef _WIN32 16 | WSADATA wsa; 17 | if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) 18 | { 19 | return -1; 20 | } 21 | #endif 22 | 23 | char* plc_ip = "127.0.0.1"; 24 | int plc_port = 102; 25 | if (argc > 1) 26 | { 27 | plc_ip = argv[1]; 28 | plc_port = atoi(argv[2]); 29 | } 30 | 31 | printf("%ld\n", sizeof(double)); 32 | 33 | int fd = -1; 34 | bool ret = s7_connect(plc_ip, plc_port, S1200, &fd); 35 | if (ret && fd > 0) 36 | { 37 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 38 | 39 | char* type = NULL; 40 | ret = s7_read_plc_type(fd, &type); 41 | printf("plc type: %s\n", type); 42 | RELEASE_DATA(type); 43 | 44 | const int TEST_COUNT = 5000; 45 | const int TEST_SLEEP_TIME = 1000; 46 | int faild_count = 0; 47 | char address[50] = { 0 }; 48 | int i = 0; 49 | 50 | for (i = 0; i < TEST_COUNT; i++) 51 | { 52 | printf("==============Test count: %d==============\n", i + 1); 53 | bool all_success = false; 54 | ////////////////////////////////////////////////////////////////////////// 55 | bool val = true; 56 | strcpy(address, "MX100"); 57 | ret = s7_write_bool(fd, address, val); 58 | printf("Write\t %s \tbool:\t %d, \tret: %d\n", address, val, ret); 59 | GET_RESULT(ret); 60 | 61 | val = false; 62 | ret = s7_read_bool(fd, address, &val); 63 | printf("Read\t %s \tbool:\t %d\n", address, val); 64 | GET_RESULT(ret); 65 | 66 | ////////////////////////////////////////////////////////////////////////// 67 | byte w_b_val = 23; 68 | strcpy(address, "MB100"); 69 | ret = s7_write_byte(fd, address, w_b_val); 70 | printf("Write\t %s \tbyte:\t %d, \tret: %d\n", address, w_b_val, ret); 71 | GET_RESULT(ret); 72 | 73 | byte b_val = 0; 74 | ret = s7_read_byte(fd, address, &b_val); 75 | printf("Read\t %s \tbyte:\t %d\n", address, b_val); 76 | GET_RESULT(ret); 77 | 78 | ////////////////////////////////////////////////////////////////////////// 79 | short w_s_val = -223; 80 | strcpy(address, "MW100"); 81 | ret = s7_write_short(fd, address, w_s_val); 82 | printf("Write\t %s \tshort:\t %d, \tret: %d\n", address, w_s_val, ret); 83 | GET_RESULT(ret); 84 | 85 | short s_val = 0; 86 | ret = s7_read_short(fd, address, &s_val); 87 | printf("Read\t %s \tshort:\t %d\n", address, s_val); 88 | GET_RESULT(ret); 89 | 90 | ////////////////////////////////////////////////////////////////////////// 91 | ushort w_us_val = 22255; 92 | strcpy(address, "MW100"); 93 | ret = s7_write_ushort(fd, address, w_us_val); 94 | printf("Write\t %s \tushort:\t %d, \tret: %d\n", address, w_us_val, ret); 95 | GET_RESULT(ret); 96 | 97 | ushort us_val = 0; 98 | ret = s7_read_ushort(fd, address, &us_val); 99 | printf("Read\t %s \tushort:\t %d\n", address, us_val); 100 | GET_RESULT(ret); 101 | 102 | ////////////////////////////////////////////////////////////////////////// 103 | int32 w_i_val = -12345; 104 | strcpy(address, "DB1.70"); 105 | ret = s7_write_int32(fd, address, w_i_val); 106 | printf("Write\t %s \tint32:\t %d, \tret: %d\n", address, w_i_val, ret); 107 | GET_RESULT(ret); 108 | 109 | int i_val = 0; 110 | ret = s7_read_int32(fd, address, &i_val); 111 | printf("Read\t %s \tint32:\t %d\n", address, i_val); 112 | GET_RESULT(ret); 113 | 114 | ////////////////////////////////////////////////////////////////////////// 115 | uint32 w_ui_val = 22345; 116 | ret = s7_write_uint32(fd, address, w_ui_val); 117 | printf("Write\t %s \tuint32:\t %d, \tret: %d\n", address, w_ui_val, ret); 118 | GET_RESULT(ret); 119 | 120 | uint32 ui_val = 0; 121 | ret = s7_read_uint32(fd, address, &ui_val); 122 | printf("Read\t %s \tuint32:\t %d\n", address, ui_val); 123 | GET_RESULT(ret); 124 | 125 | ////////////////////////////////////////////////////////////////////////// 126 | int64 w_i64_val = -333334554; 127 | strcpy(address, "DB1.DBD70"); 128 | ret = s7_write_int64(fd, address, w_i64_val); 129 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_i64_val, ret); 130 | GET_RESULT(ret); 131 | 132 | int64 i64_val = 0; 133 | ret = s7_read_int64(fd, address, &i64_val); 134 | printf("Read\t %s \tint64:\t %lld\n", address, i64_val); 135 | GET_RESULT(ret); 136 | 137 | ////////////////////////////////////////////////////////////////////////// 138 | uint64 w_ui64_val = 4333334554; 139 | strcpy(address, "DB1.DBD70"); 140 | ret = s7_write_uint64(fd, address, w_ui64_val); 141 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_ui64_val, ret); 142 | GET_RESULT(ret); 143 | 144 | int64 ui64_val = 0; 145 | ret = s7_read_uint64(fd, address, &ui64_val); 146 | printf("Read\t %s \tuint64:\t %lld\n", address, ui64_val); 147 | GET_RESULT(ret); 148 | 149 | ////////////////////////////////////////////////////////////////////////// 150 | float w_f_val = 32.454f; 151 | strcpy(address, "DB1.DBD70"); 152 | ret = s7_write_float(fd, address, w_f_val); 153 | printf("Write\t %s \tfloat:\t %f, \tret: %d\n", address, w_f_val, ret); 154 | GET_RESULT(ret); 155 | 156 | float f_val = 0; 157 | ret = s7_read_float(fd, address, &f_val); 158 | printf("Read\t %s \tfloat:\t %f\n", address, f_val); 159 | GET_RESULT(ret); 160 | 161 | ////////////////////////////////////////////////////////////////////////// 162 | double w_d_val = -12345.6789; 163 | ret = s7_write_double(fd, address, w_d_val); 164 | printf("Write\t %s \tdouble:\t %lf, \tret: %d\n", address, w_d_val, ret); 165 | GET_RESULT(ret); 166 | 167 | double d_val = 0; 168 | ret = s7_read_double(fd, address, &d_val); 169 | printf("Read\t %s \tdouble:\t %lf\n", address, d_val); 170 | GET_RESULT(ret); 171 | 172 | ////////////////////////////////////////////////////////////////////////// 173 | const char sz_write[] = "wqliceman@gmail.com"; 174 | int length = sizeof(sz_write) / sizeof(sz_write[0]); 175 | ret = s7_write_string(fd, address, length, sz_write); 176 | printf("Write\t %s \tstring:\t %s, \tret: %d\n", address, sz_write, ret); 177 | GET_RESULT(ret); 178 | 179 | char* str_val = NULL; 180 | ret = s7_read_string(fd, address, length, &str_val); 181 | printf("Read\t %s \tstring:\t %s\n", address, str_val); 182 | free(str_val); 183 | GET_RESULT(ret); 184 | 185 | #ifdef _WIN32 186 | Sleep(TEST_SLEEP_TIME); 187 | #else 188 | usleep(TEST_SLEEP_TIME * 1000); 189 | #endif 190 | } 191 | 192 | printf("All Failed count: %d\n", faild_count); 193 | 194 | //mc_remote_run(fd); 195 | //mc_remote_stop(fd); 196 | //mc_remote_reset(fd); 197 | s7_disconnect(fd); 198 | 199 | system("pause"); 200 | } 201 | 202 | #ifdef _WIN32 203 | WSACleanup(); 204 | #endif 205 | } -------------------------------------------------------------------------------- /siemens_plc_s7_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 | bytes[0] = (byte)(i & 0xFF); 29 | bytes[1] = (byte)((i >> 8) & 0xFF); 30 | } 31 | 32 | short bytes2short(byte* bytes) { 33 | return (short)(bytes[0] & 0xFF | (bytes[1] << 8)); 34 | } 35 | 36 | void ushort2bytes(ushort i, byte* bytes) { 37 | bytes[0] = (byte)(i & 0xFF); 38 | bytes[1] = (byte)((i >> 8) & 0xFF); 39 | } 40 | 41 | ushort bytes2ushort(byte* bytes) { 42 | return (ushort)(bytes[0] & 0xFF | (bytes[1] << 8)); 43 | } 44 | 45 | void int2bytes(int32 i, byte* bytes) { 46 | for (int j = 0; j < 4; j++) { 47 | bytes[j] = (byte)((i >> (j * 8)) & 0xFF); 48 | } 49 | } 50 | 51 | int32 bytes2int32(byte* bytes) { 52 | int32 iRetVal = 0; 53 | for (int j = 0; j < 4; j++) { 54 | iRetVal |= ((int32)bytes[j] << (j * 8)); 55 | } 56 | return iRetVal; 57 | } 58 | 59 | void uint2bytes(uint32 i, byte* bytes) { 60 | for (int j = 0; j < 4; j++) { 61 | bytes[j] = (byte)((i >> (j * 8)) & 0xFF); 62 | } 63 | } 64 | 65 | uint32 bytes2uint32(byte* bytes) { 66 | uint32 iRetVal = 0; 67 | for (int j = 0; j < 4; j++) { 68 | iRetVal |= ((uint32)bytes[j] << (j * 8)); 69 | } 70 | return iRetVal; 71 | } 72 | 73 | void bigInt2bytes(int64 i, byte* bytes) { 74 | for (int j = 0; j < 8; j++) { 75 | bytes[j] = (byte)((i >> (j * 8)) & 0xFF); 76 | } 77 | } 78 | 79 | int64 bytes2bigInt(byte* bytes) { 80 | int64 iRetVal = 0; 81 | for (int j = 0; j < 8; j++) { 82 | iRetVal |= ((int64)bytes[j] << (j * 8)); 83 | } 84 | return iRetVal; 85 | } 86 | 87 | void ubigInt2bytes(uint64 i, byte* bytes) { 88 | for (int j = 0; j < 8; j++) { 89 | bytes[j] = (byte)((i >> (j * 8)) & 0xFF); 90 | } 91 | } 92 | 93 | uint64 bytes2ubigInt(byte* bytes) { 94 | uint64 iRetVal = 0; 95 | for (int j = 0; j < 8; j++) { 96 | iRetVal |= ((uint64)bytes[j] << (j * 8)); 97 | } 98 | return iRetVal; 99 | } 100 | 101 | void float2bytes(float i, byte* bytes) { 102 | int temp = *(int*)&i; 103 | int2bytes(temp, bytes); 104 | } 105 | 106 | float bytes2float(byte* bytes) { 107 | int temp = bytes2int32(bytes); 108 | return *(float*)&temp; 109 | } 110 | 111 | void double2bytes(double i, byte* bytes) { 112 | int64 temp = *(int64*)&i; 113 | bigInt2bytes(temp, bytes); 114 | } 115 | 116 | double bytes2double(byte* bytes) { 117 | int64 temp = bytes2bigInt(bytes); 118 | return *(double*)&temp; 119 | } 120 | 121 | int str_to_int(const char* address) 122 | { 123 | int ret = 0; 124 | ret = (int)strtol(address, NULL, 10); 125 | return ret; 126 | } 127 | 128 | void str_toupper(char* input) 129 | { 130 | if (input == NULL) 131 | return; 132 | 133 | int32 len = strlen(input), i = 0; 134 | for (; i < len; i++) 135 | input[i] = toupper(input[i]); 136 | } 137 | 138 | void str_tolower(char* input) 139 | { 140 | if (input == NULL) 141 | return; 142 | 143 | int32 len = strlen(input), i = 0; 144 | for (; i < len; i++) 145 | input[i] = tolower(input[i]); 146 | } 147 | 148 | /** 149 | * ×Ö·û´®originÒÔ×Ö·û´®prefix¿ªÍ·£¬·µ»Ø0£»·ñÔò·µ»Ø1£»Òì³£·µ»Ø-1 150 | */ 151 | int str_start_with(const char* origin, char* prefix) 152 | { 153 | if (origin == NULL || 154 | prefix == NULL || 155 | strlen(prefix) > strlen(origin)) 156 | { 157 | return -1; 158 | } 159 | 160 | int n = strlen(prefix), i; 161 | for (i = 0; i < n; i++) { 162 | if (origin[i] != prefix[i]) { 163 | return 1; 164 | } 165 | } 166 | return 0; 167 | } 168 | 169 | /** 170 | * ×Ö·û´®originÒÔ×Ö·û´®end½á⣬·µ»Ø0£»·ñÔò·µ»Ø1£»Òì³£·µ»Ø-1 171 | */ 172 | int str_end_with(const char* origin, char* end) 173 | { 174 | if (origin == NULL || 175 | end == NULL || 176 | strlen(end) > strlen(origin)) 177 | { 178 | return -1; 179 | } 180 | 181 | int n = strlen(end); 182 | int m = strlen(origin); 183 | int i; 184 | for (i = 0; i < n; i++) 185 | { 186 | if (origin[m - i - 1] != end[n - i - 1]) 187 | return 1; 188 | } 189 | return 0; 190 | } 191 | 192 | uint32 htonf_(float value) 193 | { 194 | uint32 Tempval; 195 | uint32 Retval; 196 | Tempval = *(uint32*)(&value); 197 | Retval = _WS2_32_WINSOCK_SWAP_LONG(Tempval); 198 | return Retval; 199 | } 200 | 201 | float ntohf_(uint32 value) 202 | { 203 | const uint32 Tempval = _WS2_32_WINSOCK_SWAP_LONG(value); 204 | float Retval; 205 | *((uint32*)&Retval) = Tempval; 206 | return Retval; 207 | } 208 | 209 | uint64 htond_(double value) 210 | { 211 | uint64 Tempval; 212 | uint64 Retval; 213 | Tempval = *(uint64*)(&value); 214 | Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Tempval); 215 | return Retval; 216 | } 217 | 218 | double ntohd_(uint64 value) 219 | { 220 | const uint64 Tempval = _WS2_32_WINSOCK_SWAP_LONGLONG(value); 221 | double Retval; 222 | *((uint64*)&Retval) = Tempval; 223 | return Retval; 224 | } 225 | 226 | uint64 htonll_(uint64 Value) 227 | { 228 | const uint64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Value); 229 | return Retval; 230 | } 231 | 232 | uint64 ntohll_(uint64 Value) 233 | { 234 | const uint64 Retval = _WS2_32_WINSOCK_SWAP_LONGLONG(Value); 235 | return Retval; 236 | } 237 | 238 | #ifndef _WIN32 239 | /* 240 | ============= 241 | itoa 242 | 243 | Convert integer to string 244 | 245 | PARAMS: 246 | - value A 64-bit number to convert 247 | - str Destination buffer; should be 66 characters long for radix2, 24 - radix8, 22 - radix10, 18 - radix16. 248 | - radix Radix must be in range -36 .. 36. Negative values used for signed numbers. 249 | ============= 250 | */ 251 | 252 | char* itoa(unsigned long long value, char str[], int radix) 253 | { 254 | char buf[66]; 255 | char* dest = buf + sizeof(buf); 256 | bool sign = false; 257 | 258 | if (value == 0) { 259 | memcpy(str, "0", 2); 260 | return str; 261 | } 262 | 263 | if (radix < 0) { 264 | radix = -radix; 265 | if ((long long)value < 0) { 266 | value = -value; 267 | sign = true; 268 | } 269 | } 270 | 271 | *--dest = '\0'; 272 | 273 | switch (radix) 274 | { 275 | case 16: 276 | while (value) { 277 | *--dest = '0' + (value & 0xF); 278 | if (*dest > '9') *dest += 'A' - '9' - 1; 279 | value >>= 4; 280 | } 281 | break; 282 | case 10: 283 | while (value) { 284 | *--dest = '0' + (value % 10); 285 | value /= 10; 286 | } 287 | break; 288 | 289 | case 8: 290 | while (value) { 291 | *--dest = '0' + (value & 7); 292 | value >>= 3; 293 | } 294 | break; 295 | 296 | case 2: 297 | while (value) { 298 | *--dest = '0' + (value & 1); 299 | value >>= 1; 300 | } 301 | break; 302 | 303 | default: // The slow version, but universal 304 | while (value) { 305 | *--dest = '0' + (value % radix); 306 | if (*dest > '9') *dest += 'A' - '9' - 1; 307 | value /= radix; 308 | } 309 | break; 310 | } 311 | 312 | if (sign) *--dest = '-'; 313 | 314 | memcpy(str, dest, buf + sizeof(buf) - dest); 315 | return str; 316 | } 317 | #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 | *.o 366 | *.d -------------------------------------------------------------------------------- /siemens_plc_s7_net/simens_plc_s7_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.22621.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | MultiByte 34 | 35 | 36 | Application 37 | false 38 | v140 39 | true 40 | MultiByte 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v140 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 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | NotUsing 88 | Level3 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 92 | true 93 | pch.h 94 | 4966 95 | MultiThreadedDebug 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | NotUsing 105 | Level3 106 | Disabled 107 | true 108 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 109 | true 110 | pch.h 111 | 4966 112 | 113 | 114 | Console 115 | true 116 | 117 | 118 | 119 | 120 | NotUsing 121 | Level3 122 | MaxSpeed 123 | true 124 | true 125 | true 126 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 127 | true 128 | pch.h 129 | 4966 130 | MultiThreaded 131 | 132 | 133 | Console 134 | true 135 | true 136 | true 137 | 138 | 139 | 140 | 141 | NotUsing 142 | Level3 143 | MaxSpeed 144 | true 145 | true 146 | true 147 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS;_WINSOCK_SECURE_NO_WARNINGS 148 | true 149 | pch.h 150 | 4966 151 | 152 | 153 | Console 154 | true 155 | true 156 | true 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English README](README_EN.md) 2 | 3 | # 程序整体介绍 4 | 5 | - 项目名称:siemens_plc_s7_net 6 | - 开发语言:C语言 7 | - 支持操作系统:windows/linux 8 | - 测试设备:S1200 9 | 10 | 目前实现功能,实现西门子PLC通讯类,采用S7协议实现,需要在PLC侧先的以太网模块先进行配置。 11 | 12 | ## 头文件 13 | 14 | ```c 15 | #include "siemens_s7.h" //协议提供方法接口 16 | #include "typedef.h" //部分类型宏定义 17 | ``` 18 | 19 | ## 西门子PLC地址说明 20 | 21 | ### 连接属性 22 | 23 | - port: 端口号,通常为102 24 | - plc_type: plc型号,S200、S200Smart、S300、S400、S1200、S1500 25 | 26 | ### PLC地址分类 27 | 28 | 类型的代号值(软元件代码,用于区分软元件类型,如:D,R) 29 | 30 | | 序号 | 描述 | 地址类型 | 31 | | :---: | :------------- | -------- | 32 | | 1 | 中间继电器 | M | 33 | | 2 | 输入继电器 | I | 34 | | 3 | 输出继电器Q | Q | 35 | | 4 | DB块寄存器DB | DB | 36 | | 5 | V寄存器 | V | 37 | | 6 | 定时器的值 | T | 38 | | 7 | 计数器的值 | C | 39 | | 8 | 智能输入寄存器 | AI | 40 | | 9 | 智能输出寄存器 | AQ | 41 | 42 | ## 实现方法 43 | 44 | ### 1.连接PLC设备 45 | 46 | ```c 47 | bool s7_connect(char* ip_addr, int port, siemens_plc_types_e plc, int* fd); 48 | /* 连接PLC设备 49 | * 参数: 50 | * ip_addr: PLC的IP地址 51 | * port: PLC的端口号 52 | * plc: PLC的型号 53 | * fd: 连接成功后返回的文件描述符 54 | * 返回值: 55 | * 连接成功返回true,失败返回false 56 | */ 57 | 58 | bool s7_disconnect(int fd); 59 | /* 断开与PLC的连接 60 | * 参数: 61 | * fd: 连接PLC的文件描述符 62 | * 返回值: 63 | * 成功返回true,失败返回false 64 | */ 65 | 66 | byte get_plc_slot(); 67 | /* 获取PLC的槽号 68 | * 返回值: 69 | * PLC的槽号 70 | */ 71 | 72 | void set_plc_slot(byte slot); 73 | /* 设置PLC的槽号 74 | * 参数: 75 | * slot: 要设置的槽号 76 | */ 77 | 78 | byte get_plc_rack(); 79 | /* 获取PLC的机架号 80 | * 返回值: 81 | * PLC的机架号 82 | */ 83 | 84 | void set_plc_rack(byte rack); 85 | /* 设置PLC的机架号 86 | * 参数: 87 | * rack: 要设置的机架号 88 | */ 89 | 90 | byte get_plc_connection_type(); 91 | /* 获取PLC的连接类型 92 | * 返回值: 93 | * PLC的连接类型 94 | */ 95 | 96 | void set_plc_connection_type(byte rack); 97 | /* 设置PLC的连接类型 98 | * 参数: 99 | * rack: 要设置的连接类型 100 | */ 101 | 102 | int get_plc_local_TSAP(); 103 | /* 获取PLC的本地TSAP 104 | * 返回值: 105 | * PLC的本地TSAP 106 | */ 107 | 108 | void set_plc_local_TSAP(int tasp); 109 | /* 设置PLC的本地TSAP 110 | * 参数: 111 | * tasp: 要设置的本地TSAP 112 | */ 113 | 114 | int get_plc_dest_TSAP(); 115 | /* 获取PLC的目标TSAP 116 | * 返回值: 117 | * PLC的目标TSAP 118 | */ 119 | 120 | void set_plc_dest_TSAP(int tasp); 121 | /* 设置PLC的目标TSAP 122 | * 参数: 123 | * tasp: 要设置的目标TSAP 124 | */ 125 | 126 | int get_plc_PDU_length(); 127 | /* 获取PLC的PDU长度 128 | * 返回值: 129 | * PLC的PDU长度 130 | */ 131 | ``` 132 | 133 | ### 2.读取数据 134 | 135 | ```c 136 | S7_error_code_e s7_read_bool(int fd, const char* address, bool* val); 137 | /* 从PLC读取布尔值数据 138 | * 参数: 139 | * fd: 连接PLC的文件描述符 140 | * address: 数据在PLC中的地址 141 | * val: 用于接收读取到的数据的指针 142 | * 返回值: 143 | * 读取成功返回S7_ERROR_CODE_SUCCESS,否则返回相应的错误码 144 | */ 145 | 146 | s7_error_code_e s7_read_byte(int fd, const char* address, byte* val); 147 | /* 从PLC读取字节数据 148 | * 参数同上,此处省略... 149 | */ 150 | 151 | s7_error_code_e s7_read_short(int fd, const char* address, short* val); 152 | /* 从PLC读取短整型数据 153 | * 参数同上,此处省略... 154 | */ 155 | 156 | s7_error_code_e s7_read_ushort(int fd, const char* address, ushort* val); 157 | /* 从PLC读取无符号短整型数据 158 | * 参数同上,此处省略... 159 | */ 160 | 161 | s7_error_code_e s7_read_int32(int fd, const char* address, int32* val); 162 | /* 从PLC读取32位整型数据 163 | * 参数同上,此处省略... 164 | */ 165 | 166 | s7_error_code_e s7_read_uint32(int fd, const char* address, uint32* val); 167 | /* 从PLC读取无符号32位整型数据 168 | * 参数同上,此处省略... 169 | */ 170 | 171 | s7_error_code_e s7_read_int64(int fd, const char* address, int64* val); 172 | /* 从PLC读取64位整型数据 173 | * 参数同上,此处省略... 174 | */ 175 | 176 | s7_error_code_e s7_read_uint64(int fd, const char* address, uint64* val); 177 | /* 从PLC读取无符号64位整型数据 178 | * 参数同上,此处省略... 179 | */ 180 | 181 | s7_error_code_e s7_read_float(int fd, const char* address, float* val); 182 | /* 从PLC读取浮点型数据 183 | * 参数同上,此处省略... 184 | */ 185 | 186 | s7_error_code_e s7_read_double(int fd, const char* address, double* val); 187 | /* 从PLC读取双精度浮点型数据 188 | * 参数同上,此处省略... 189 | */ 190 | 191 | s7_error_code_e s7_read_string(int fd, const char* address, int length, char** val); 192 | /* 从PLC读取字符串数据,需要手动释放返回的字符串内存 193 | * 参数: 194 | * fd: 连接PLC的文件描述符 195 | * address: 数据在PLC中的地址 196 | * length: 要读取的字符串长度 197 | * val: 用于接收读取到的字符串的指针,使用后需释放内存 198 | * 返回值: 199 | * 读取成功返回S7_ERROR_CODE_SUCCESS,否则返回相应的错误码 200 | */ 201 | ``` 202 | 203 | ### 3.写入数据 204 | 205 | ```c 206 | s7_error_code_e s7_write_bool(int fd, const char* address, bool val); 207 | /* 向PLC写入布尔值数据 208 | * 参数同上,此处省略... 209 | */ 210 | 211 | s7_error_code_e s7_write_byte(int fd, const char* address, byte val); 212 | /* 向PLC写入字节数据 213 | * 参数同上,此处省略... 214 | */ 215 | 216 | s7_error_code_e s7_write_short(int fd, const char* address, short val); 217 | /* 向PLC写入短整型数据 218 | * 参数同上,此处省略... 219 | */ 220 | 221 | s7_error_code_e s7_write_ushort(int fd, const char* address, ushort val); 222 | /* 向PLC写入无符号短整型数据 223 | * 参数同上,此处省略... 224 | */ 225 | 226 | s7_error_code_e s7_write_int32(int fd, const char* address, int32 val); 227 | /* 向PLC写入32位整型数据 228 | * 参数同上,此处省略... 229 | */ 230 | 231 | s7_error_code_e s7_write_uint32(int fd, const char* address, uint32 val); 232 | /* 向PLC写入无符号32位整型数据 233 | * 参数同上,此处省略... 234 | */ 235 | 236 | s7_error_code_e s7_write_int64(int fd, const char* address, int64 val); 237 | /* 向PLC写入64位整型数据 238 | * 参数同上,此处省略... 239 | */ 240 | 241 | s7_error_code_e s7_write_uint64(int fd, const char* address, uint64 val); 242 | /* 向PLC写入无符号64位整型数据 243 | * 参数同上,此处省略... 244 | */ 245 | 246 | s7_error_code_e s7_write_float(int fd, const char* address, float val); 247 | /* 向PLC写入浮点型数据 248 | * 参数同上,此处省略... 249 | */ 250 | 251 | s7_error_code_e s7_write_double(int fd, const char* address, double val); 252 | /* 向PLC写入双精度浮点型数据 253 | * 参数同上,此处省略... 254 | */ 255 | 256 | s7_error_code_e s7_write_string(int fd, const char* address, int length, const char* val); 257 | /* 向PLC写入字符串数据 258 | * 参数: 259 | * fd: 连接PLC的文件描述符 260 | * address: 数据在PLC中的地址 261 | * length: 要写入的字符串长度 262 | * val: 要写入的字符串 263 | * 返回值: 264 | * 写入成功返回S7_ERROR_CODE_SUCCESS,否则返回相应的错误码 265 | */ 266 | ``` 267 | 268 | ## 使用样例 269 | 270 | 完整样例参见代码中**main.c**文件,如下提供主要代码和使用方法: 271 | 272 | *读取地址,格式为"**M100**","**DB100**" 273 | 274 | ```c 275 | #ifdef _WIN32 276 | #include 277 | #endif 278 | #include 279 | #include 280 | #pragma warning( disable : 4996) 281 | 282 | #define GET_RESULT(ret){ if(!ret) faild_count++; } 283 | 284 | #include "siemens_s7.h" 285 | 286 | int main(int argc, char** argv) 287 | { 288 | #ifdef _WIN32 289 | WSADATA wsa; 290 | if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) 291 | { 292 | return -1; 293 | } 294 | #endif 295 | 296 | char* plc_ip = "192.168.123.170"; 297 | int plc_port = 102; 298 | if (argc > 1) 299 | { 300 | plc_ip = argv[1]; 301 | plc_port = atoi(argv[2]); 302 | } 303 | 304 | int fd = -1; 305 | bool ret = s7_connect(plc_ip, plc_port, S1200, &fd); 306 | if (ret && fd > 0) 307 | { 308 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 309 | 310 | char* type = s7_read_plc_type(fd); 311 | printf("plc type: %s\n", type); 312 | free(type); 313 | 314 | const int TEST_COUNT = 5000; 315 | const int TEST_SLEEP_TIME = 1000; 316 | int faild_count = 0; 317 | char address[50] = { 0 }; 318 | int i = 0; 319 | 320 | for (i = 0; i < TEST_COUNT; i++) 321 | { 322 | printf("==============Test count: %d==============\n", i + 1); 323 | bool all_success = false; 324 | ////////////////////////////////////////////////////////////////////////// 325 | bool val = true; 326 | strcpy(address, "MX100"); 327 | ret = s7_write_bool(fd, address, val); 328 | printf("Write\t %s \tbool:\t %d, \tret: %d\n", address, val, ret); 329 | GET_RESULT(ret); 330 | 331 | val = false; 332 | ret = s7_read_bool(fd, address, &val); 333 | printf("Read\t %s \tbool:\t %d\n", address, val); 334 | GET_RESULT(ret); 335 | 336 | ////////////////////////////////////////////////////////////////////////// 337 | short w_s_val = 23; 338 | strcpy(address, "MW100"); 339 | ret = s7_write_short(fd, address, w_s_val); 340 | printf("Write\t %s \tshort:\t %d, \tret: %d\n", address, w_s_val, ret); 341 | GET_RESULT(ret); 342 | 343 | short s_val = 0; 344 | ret = s7_read_short(fd, address, &s_val); 345 | printf("Read\t %s \tshort:\t %d\n", address, s_val); 346 | GET_RESULT(ret); 347 | 348 | ////////////////////////////////////////////////////////////////////////// 349 | ushort w_us_val = 255; 350 | strcpy(address, "MW100"); 351 | ret = s7_write_ushort(fd, address, w_us_val); 352 | printf("Write\t %s \tushort:\t %d, \tret: %d\n", address, w_us_val, ret); 353 | GET_RESULT(ret); 354 | 355 | ushort us_val = 0; 356 | ret = s7_read_ushort(fd, address, &us_val); 357 | printf("Read\t %s \tushort:\t %d\n", address, us_val); 358 | GET_RESULT(ret); 359 | 360 | ////////////////////////////////////////////////////////////////////////// 361 | int32 w_i_val = 12345; 362 | strcpy(address, "DB1.70"); 363 | ret = s7_write_int32(fd, address, w_i_val); 364 | printf("Write\t %s \tint32:\t %d, \tret: %d\n", address, w_i_val, ret); 365 | GET_RESULT(ret); 366 | 367 | int i_val = 0; 368 | ret = s7_read_int32(fd, address, &i_val); 369 | printf("Read\t %s \tint32:\t %d\n", address, i_val); 370 | GET_RESULT(ret); 371 | 372 | ////////////////////////////////////////////////////////////////////////// 373 | uint32 w_ui_val = 22345; 374 | ret = s7_write_uint32(fd, address, w_ui_val); 375 | printf("Write\t %s \tuint32:\t %d, \tret: %d\n", address, w_ui_val, ret); 376 | GET_RESULT(ret); 377 | 378 | uint32 ui_val = 0; 379 | ret = s7_read_uint32(fd, address, &ui_val); 380 | printf("Read\t %s \tuint32:\t %d\n", address, ui_val); 381 | GET_RESULT(ret); 382 | 383 | ////////////////////////////////////////////////////////////////////////// 384 | int64 w_i64_val = 333334554; 385 | strcpy(address, "DB1.DBW70"); 386 | ret = s7_write_int64(fd, address, w_i64_val); 387 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_i64_val, ret); 388 | GET_RESULT(ret); 389 | 390 | int64 i64_val = 0; 391 | ret = s7_read_int64(fd, address, &i64_val); 392 | printf("Read\t %s \tint64:\t %lld\n", address, i64_val); 393 | GET_RESULT(ret); 394 | 395 | ////////////////////////////////////////////////////////////////////////// 396 | uint64 w_ui64_val = 4333334554; 397 | strcpy(address, "DB1.DBW70"); 398 | ret = s7_write_uint64(fd, address, w_ui64_val); 399 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_ui64_val, ret); 400 | GET_RESULT(ret); 401 | 402 | int64 ui64_val = 0; 403 | ret = s7_read_uint64(fd, address, &ui64_val); 404 | printf("Read\t %s \tuint64:\t %lld\n", address, ui64_val); 405 | GET_RESULT(ret); 406 | 407 | ////////////////////////////////////////////////////////////////////////// 408 | float w_f_val = 32.454f; 409 | strcpy(address, "DB1.DBW70"); 410 | ret = s7_write_float(fd, address, w_f_val); 411 | printf("Write\t %s \tfloat:\t %f, \tret: %d\n", address, w_f_val, ret); 412 | GET_RESULT(ret); 413 | 414 | float f_val = 0; 415 | ret = s7_read_float(fd, address, &f_val); 416 | printf("Read\t %s \tfloat:\t %f\n", address, f_val); 417 | GET_RESULT(ret); 418 | 419 | ////////////////////////////////////////////////////////////////////////// 420 | double w_d_val = 12345.6789; 421 | ret = s7_write_double(fd, address, w_d_val); 422 | printf("Write\t %s \tdouble:\t %lf, \tret: %d\n", address, w_d_val, ret); 423 | GET_RESULT(ret); 424 | 425 | double d_val = 0; 426 | ret = s7_read_double(fd, address, &d_val); 427 | printf("Read\t %s \tdouble:\t %lf\n", address, d_val); 428 | GET_RESULT(ret); 429 | 430 | ////////////////////////////////////////////////////////////////////////// 431 | const char sz_write[] = "wqliceman@gmail.com"; 432 | int length = sizeof(sz_write) / sizeof(sz_write[0]); 433 | ret = s7_write_string(fd, address, length, sz_write); 434 | printf("Write\t %s \tstring:\t %s, \tret: %d\n", address, sz_write, ret); 435 | GET_RESULT(ret); 436 | 437 | char* str_val = NULL; 438 | ret = s7_read_string(fd, address, length, &str_val); 439 | printf("Read\t %s \tstring:\t %s\n", address, str_val); 440 | free(str_val); 441 | GET_RESULT(ret); 442 | 443 | #ifdef _WIN32 444 | Sleep(TEST_SLEEP_TIME); 445 | #else 446 | usleep(TEST_SLEEP_TIME* 1000); 447 | #endif 448 | } 449 | 450 | printf("All Failed count: %d\n", faild_count); 451 | 452 | //mc_remote_run(fd); 453 | //mc_remote_stop(fd); 454 | //mc_remote_reset(fd); 455 | s7_disconnect(fd); 456 | 457 | system("pause"); 458 | } 459 | 460 | #ifdef _WIN32 461 | WSACleanup(); 462 | #endif 463 | } 464 | ``` 465 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # Overall Program Introduction 2 | 3 | - Project Name: siemens_plc_s7_net 4 | - Development Language: C 5 | - Supported Operating Systems: Windows/Linux 6 | - Test Device: S1200 7 | Currently implemented functionality includes a Siemens PLC communication class utilizing the S7 protocol. Configuration of the Ethernet module on the PLC side is required beforehand. 8 | 9 | ## Header Files 10 | 11 | ```c 12 | #include "siemens_s7.h" // Provides method interfaces for the protocol 13 | #include "typedef.h" // Contains some macro definitions for types 14 | ``` 15 | 16 | ## Siemens PLC Address Description 17 | 18 | ### Connection Attributes 19 | 20 | - port: Port number, typically 102 21 | - plc_type: PLC model, such as S200, S200Smart, S300, S400, S1200, S1500 22 | 23 | ### PLC Address Classification 24 | 25 | Code values for types (soft element codes used to distinguish soft element types, e.g., D, R) 26 | 27 | | Serial No. | Description | Address Type | 28 | | :---: | :------------- | -------- | 29 | | 1 | Intermediate relay | M | 30 | | 2 | Input relay | I | 31 | | 3 | Output relay (Q) | Q | 32 | | 4 | DB block register (DB) | DB | 33 | | 5 | V register | V | 34 | | 6 | Timer value | T | 35 | | 7 | Counter value | C | 36 | | 8 | Intelligent input relay | AI | 37 | | 9 | Intelligent output relay | AQ | 38 | 39 | ## Implemented Methods 40 | 41 | ### 1. Connecting to PLC Devices 42 | 43 | ```c 44 | bool s7_connect(char* ip_addr, int port, siemens_plc_types_e plc, int* fd); 45 | /* Connects to a PLC device 46 | * Parameters: 47 | * ip_addr: The IP address of the PLC 48 | * port: The port number of the PLC 49 | * plc: The model of the PLC 50 | * fd: Upon successful connection, returns the file descriptor 51 | * Return Value: 52 | * Returns true if the connection succeeds, false otherwise 53 | */ 54 | 55 | bool s7_disconnect(int fd); 56 | /* Disconnects from the PLC 57 | * Parameters: 58 | * fd: The file descriptor used to connect to the PLC 59 | * Return Value: 60 | * Returns true if the disconnection succeeds, false otherwise 61 | */ 62 | 63 | byte get_plc_slot(); 64 | /* Retrieves the slot number of the PLC 65 | * Return Value: 66 | * Returns the slot number of the PLC 67 | */ 68 | 69 | void set_plc_slot(byte slot); 70 | /* Sets the slot number of the PLC 71 | * Parameters: 72 | * slot: The desired slot number to set 73 | */ 74 | 75 | byte get_plc_rack(); 76 | /* Retrieves the rack number of the PLC 77 | * Return Value: 78 | * Returns the rack number of the PLC 79 | */ 80 | 81 | void set_plc_rack(byte rack); 82 | /* Sets the rack number of the PLC 83 | * Parameters: 84 | * rack: The desired rack number to set 85 | */ 86 | 87 | byte get_plc_connection_type(); 88 | /* Retrieves the connection type of the PLC 89 | * Return Value: 90 | * Returns the connection type of the PLC 91 | */ 92 | 93 | void set_plc_connection_type(byte rack); 94 | /* Sets the connection type of the PLC 95 | * Parameters: 96 | * rack: The desired connection type to set 97 | */ 98 | 99 | int get_plc_local_TSAP(); 100 | /* Retrieves the local TSAP of the PLC 101 | * Return Value: 102 | * Returns the local TSAP of the PLC 103 | */ 104 | 105 | void set_plc_local_TSAP(int tasp); 106 | /* Sets the local TSAP of the PLC 107 | * Parameters: 108 | * tasp: The desired local TSAP to set 109 | */ 110 | 111 | int get_plc_dest_TSAP(); 112 | /* Retrieves the destination TSAP of the PLC 113 | * Return Value: 114 | * Returns the destination TSAP of the PLC 115 | */ 116 | 117 | void set_plc_dest_TSAP(int tasp); 118 | /* Sets the destination TSAP of the PLC 119 | * Parameters: 120 | * tasp: The desired destination TSAP to set 121 | */ 122 | 123 | int get_plc_PDU_length(); 124 | /* Retrieves the PDU length of the PLC 125 | * Return Value: 126 | * Returns the PDU length of the PLC 127 | */ 128 | ``` 129 | 130 | ### 2. Reading Data 131 | 132 | ```c 133 | s7_error_code_e s7_read_bool(int fd, const char* address, bool* val); 134 | /* Reads a boolean value from the PLC 135 | * Parameters: 136 | * fd: The file descriptor for the PLC connection 137 | * address: The address of the data in the PLC 138 | * val: Pointer to receive the read data 139 | * Return Value: 140 | * Returns S7_ERROR_CODE_SUCCESS if the read operation succeeds; otherwise, returns the corresponding error code 141 | */ 142 | 143 | // Similar function declarations for reading other data types (e.g., byte, short, etc.) are omitted for brevity 144 | 145 | s7_error_code_e s7_read_string(int fd, const char* address, int length, char** val); 146 | /* Reads a string from the PLC. The memory for the returned string must be manually freed. 147 | * Parameters: 148 | * fd: The file descriptor for the PLC connection 149 | * address: The address of the data in the PLC 150 | * length: The length of the string to read 151 | * val: Pointer to receive the read string; memory should be freed after use 152 | * Return Value: 153 | * Returns S7_ERROR_CODE_SUCCESS if the read operation succeeds; otherwise, returns the corresponding error code 154 | */ 155 | ``` 156 | 157 | ### 3. Writing Data 158 | 159 | ```c 160 | s7_error_code_e s7_write_bool(int fd, const char* address, bool val); 161 | /* Writes a boolean value to the PLC 162 | * Parameters: 163 | * fd: The file descriptor for the PLC connection 164 | * address: The address of the data in the PLC 165 | * val: The value to write 166 | * Return Value: 167 | * Returns S7_ERROR_CODE_SUCCESS if the write operation succeeds; otherwise, returns the corresponding error code 168 | */ 169 | 170 | // Similar function declarations for writing other data types (e.g., byte, short, etc.) are omitted for brevity 171 | 172 | s7_error_code_e s7_write_string(int fd, const char* address, int length, const char* val); 173 | /* Writes a string to the PLC 174 | * Parameters: 175 | * fd: The file descriptor for the PLC connection 176 | * address: The address of the data in the PLC 177 | * length: The length of the string to write 178 | * val: The string to write 179 | * Return Value: 180 | * Returns S7_ERROR_CODE_SUCCESS if the write operation succeeds; otherwise, returns the corresponding error code 181 | */ 182 | ``` 183 | 184 | ## Usage Example 185 | 186 | For the complete example, refer to the main.c file in the code. Below is the main code and usage method: 187 | 188 | Read addresses in the format "M100", "DB100" 189 | 190 | ```c 191 | #ifdef _WIN32 192 | #include 193 | #endif 194 | #include 195 | #include 196 | #pragma warning( disable : 4996) 197 | 198 | #define GET_RESULT(ret){ if(!ret) faild_count++; } 199 | 200 | #include "siemens_s7.h" 201 | 202 | int main(int argc, char** argv) 203 | { 204 | #ifdef _WIN32 205 | WSADATA wsa; 206 | if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) 207 | { 208 | return -1; 209 | } 210 | #endif 211 | 212 | char* plc_ip = "192.168.123.170"; 213 | int plc_port = 102; 214 | if (argc > 1) 215 | { 216 | plc_ip = argv[1]; 217 | plc_port = atoi(argv[2]); 218 | } 219 | 220 | int fd = -1; 221 | bool ret = s7_connect(plc_ip, plc_port, S1200, &fd); 222 | if (ret && fd > 0) 223 | { 224 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 225 | 226 | char* type = s7_read_plc_type(fd); 227 | printf("plc type: %s\n", type); 228 | free(type); 229 | 230 | const int TEST_COUNT = 5000; 231 | const int TEST_SLEEP_TIME = 1000; 232 | int faild_count = 0; 233 | char address[50] = { 0 }; 234 | int i = 0; 235 | 236 | for (i = 0; i < TEST_COUNT; i++) 237 | { 238 | printf("==============Test count: %d==============\n", i + 1); 239 | bool all_success = false; 240 | ////////////////////////////////////////////////////////////////////////// 241 | bool val = true; 242 | strcpy(address, "MX100"); 243 | ret = s7_write_bool(fd, address, val); 244 | printf("Write\t %s \tbool:\t %d, \tret: %d\n", address, val, ret); 245 | GET_RESULT(ret); 246 | 247 | val = false; 248 | ret = s7_read_bool(fd, address, &val); 249 | printf("Read\t %s \tbool:\t %d\n", address, val); 250 | GET_RESULT(ret); 251 | 252 | ////////////////////////////////////////////////////////////////////////// 253 | short w_s_val = 23; 254 | strcpy(address, "MW100"); 255 | ret = s7_write_short(fd, address, w_s_val); 256 | printf("Write\t %s \tshort:\t %d, \tret: %d\n", address, w_s_val, ret); 257 | GET_RESULT(ret); 258 | 259 | short s_val = 0; 260 | ret = s7_read_short(fd, address, &s_val); 261 | printf("Read\t %s \tshort:\t %d\n", address, s_val); 262 | GET_RESULT(ret); 263 | 264 | ////////////////////////////////////////////////////////////////////////// 265 | ushort w_us_val = 255; 266 | strcpy(address, "MW100"); 267 | ret = s7_write_ushort(fd, address, w_us_val); 268 | printf("Write\t %s \tushort:\t %d, \tret: %d\n", address, w_us_val, ret); 269 | GET_RESULT(ret); 270 | 271 | ushort us_val = 0; 272 | ret = s7_read_ushort(fd, address, &us_val); 273 | printf("Read\t %s \tushort:\t %d\n", address, us_val); 274 | GET_RESULT(ret); 275 | 276 | ////////////////////////////////////////////////////////////////////////// 277 | int32 w_i_val = 12345; 278 | strcpy(address, "DB1.70"); 279 | ret = s7_write_int32(fd, address, w_i_val); 280 | printf("Write\t %s \tint32:\t %d, \tret: %d\n", address, w_i_val, ret); 281 | GET_RESULT(ret); 282 | 283 | int i_val = 0; 284 | ret = s7_read_int32(fd, address, &i_val); 285 | printf("Read\t %s \tint32:\t %d\n", address, i_val); 286 | GET_RESULT(ret); 287 | 288 | ////////////////////////////////////////////////////////////////////////// 289 | uint32 w_ui_val = 22345; 290 | ret = s7_write_uint32(fd, address, w_ui_val); 291 | printf("Write\t %s \tuint32:\t %d, \tret: %d\n", address, w_ui_val, ret); 292 | GET_RESULT(ret); 293 | 294 | uint32 ui_val = 0; 295 | ret = s7_read_uint32(fd, address, &ui_val); 296 | printf("Read\t %s \tuint32:\t %d\n", address, ui_val); 297 | GET_RESULT(ret); 298 | 299 | ////////////////////////////////////////////////////////////////////////// 300 | int64 w_i64_val = 333334554; 301 | strcpy(address, "DB1.DBW70"); 302 | ret = s7_write_int64(fd, address, w_i64_val); 303 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_i64_val, ret); 304 | GET_RESULT(ret); 305 | 306 | int64 i64_val = 0; 307 | ret = s7_read_int64(fd, address, &i64_val); 308 | printf("Read\t %s \tint64:\t %lld\n", address, i64_val); 309 | GET_RESULT(ret); 310 | 311 | ////////////////////////////////////////////////////////////////////////// 312 | uint64 w_ui64_val = 4333334554; 313 | strcpy(address, "DB1.DBW70"); 314 | ret = s7_write_uint64(fd, address, w_ui64_val); 315 | printf("Write\t %s \tuint64:\t %lld, \tret: %d\n", address, w_ui64_val, ret); 316 | GET_RESULT(ret); 317 | 318 | int64 ui64_val = 0; 319 | ret = s7_read_uint64(fd, address, &ui64_val); 320 | printf("Read\t %s \tuint64:\t %lld\n", address, ui64_val); 321 | GET_RESULT(ret); 322 | 323 | ////////////////////////////////////////////////////////////////////////// 324 | float w_f_val = 32.454f; 325 | strcpy(address, "DB1.DBW70"); 326 | ret = s7_write_float(fd, address, w_f_val); 327 | printf("Write\t %s \tfloat:\t %f, \tret: %d\n", address, w_f_val, ret); 328 | GET_RESULT(ret); 329 | 330 | float f_val = 0; 331 | ret = s7_read_float(fd, address, &f_val); 332 | printf("Read\t %s \tfloat:\t %f\n", address, f_val); 333 | GET_RESULT(ret); 334 | 335 | ////////////////////////////////////////////////////////////////////////// 336 | double w_d_val = 12345.6789; 337 | ret = s7_write_double(fd, address, w_d_val); 338 | printf("Write\t %s \tdouble:\t %lf, \tret: %d\n", address, w_d_val, ret); 339 | GET_RESULT(ret); 340 | 341 | double d_val = 0; 342 | ret = s7_read_double(fd, address, &d_val); 343 | printf("Read\t %s \tdouble:\t %lf\n", address, d_val); 344 | GET_RESULT(ret); 345 | 346 | ////////////////////////////////////////////////////////////////////////// 347 | const char sz_write[] = "wqliceman@gmail.com"; 348 | int length = sizeof(sz_write) / sizeof(sz_write[0]); 349 | ret = s7_write_string(fd, address, length, sz_write); 350 | printf("Write\t %s \tstring:\t %s, \tret: %d\n", address, sz_write, ret); 351 | GET_RESULT(ret); 352 | 353 | char* str_val = NULL; 354 | ret = s7_read_string(fd, address, length, &str_val); 355 | printf("Read\t %s \tstring:\t %s\n", address, str_val); 356 | free(str_val); 357 | GET_RESULT(ret); 358 | 359 | #ifdef _WIN32 360 | Sleep(TEST_SLEEP_TIME); 361 | #else 362 | usleep(TEST_SLEEP_TIME* 1000); 363 | #endif 364 | } 365 | 366 | printf("All Failed count: %d\n", faild_count); 367 | 368 | //mc_remote_run(fd); 369 | //mc_remote_stop(fd); 370 | //mc_remote_reset(fd); 371 | s7_disconnect(fd); 372 | 373 | system("pause"); 374 | } 375 | 376 | #ifdef _WIN32 377 | WSACleanup(); 378 | #endif 379 | } 380 | ``` 381 | -------------------------------------------------------------------------------- /siemens_plc_s7_net/siemens_helper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "siemens_helper.h" 5 | #include "socket.h" 6 | 7 | #define BUFFER_SIZE 1024 8 | 9 | // 提取公共的命令头部构建逻辑 10 | void build_command_header(byte* command, ushort command_len, byte command_type) { 11 | command[0] = 0x03; 12 | command[1] = 0x00; 13 | command[2] = (byte)(command_len / 256); 14 | command[3] = (byte)(command_len % 256); 15 | command[4] = 0x02; 16 | command[5] = 0xF0; 17 | command[6] = 0x80; 18 | command[7] = 0x32; 19 | command[8] = 0x01; 20 | command[9] = 0x00; 21 | command[10] = 0x00; 22 | command[11] = 0x00; 23 | command[12] = 0x01; 24 | command[13] = (byte)((command_len - 17) / 256); 25 | command[14] = (byte)((command_len - 17) % 256); 26 | command[15] = 0x00; 27 | command[16] = 0x00; 28 | command[17] = command_type; 29 | command[18] = 0x01; 30 | } 31 | 32 | // 从地址构造核心报文 33 | byte_array_info build_read_byte_command(siemens_s7_address_data address) 34 | { 35 | const ushort command_len = 19 + 12; // head + block 36 | byte* command = (byte*)malloc(command_len); 37 | build_command_header(command, command_len, 0x04); 38 | 39 | // 指定有效值类型 -> Specify a valid value type 40 | command[19] = 0x12; 41 | // 接下来本次地址访问长度 -> The next time the address access length 42 | command[20] = 0x0A; 43 | // 语法标记,ANY -> Syntax tag, any 44 | command[21] = 0x10; 45 | // 按字为单位 -> by word 46 | if (address.data_code == 0x1E || address.data_code == 0x1F) 47 | { 48 | command[22] = address.data_code; 49 | // 访问数据的个数 -> Number of Access data 50 | command[23] = (byte)(address.length / 2 / 256); 51 | command[24] = (byte)(address.length / 2 % 256); 52 | } 53 | else 54 | { 55 | if (address.data_code == 0x06 || address.data_code == 0x07) 56 | { 57 | // 访问数据的个数 -> Number of Access data 58 | command[22] = 0x04; 59 | command[23] = (byte)(address.length / 2 / 256); 60 | command[24] = (byte)(address.length / 2 % 256); 61 | } 62 | else 63 | { 64 | command[22] = 0x02; 65 | // 访问数据的个数 -> Number of Access data 66 | command[23] = (byte)(address.length / 256); 67 | command[24] = (byte)(address.length % 256); 68 | } 69 | } 70 | // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block 71 | command[25] = (byte)(address.db_block / 256); 72 | command[26] = (byte)(address.db_block % 256); 73 | // 访问数据类型 -> Accessing data types 74 | command[27] = address.data_code; 75 | // 偏移位置 -> Offset position 76 | command[28] = (byte)(address.address_start / 256 / 256 % 256); 77 | command[29] = (byte)(address.address_start / 256 % 256); 78 | command[30] = (byte)(address.address_start % 256); 79 | 80 | byte_array_info ret = { 0 }; 81 | ret.data = command; 82 | ret.length = command_len; 83 | return ret; 84 | } 85 | 86 | byte_array_info build_read_bit_command(siemens_s7_address_data address) 87 | { 88 | const ushort command_len = 19 + 12; // head + block 89 | byte* command = (byte*)malloc(command_len); 90 | build_command_header(command, command_len, 0x04); 91 | 92 | // 读取地址的前缀 -> Read the prefix of the address 93 | command[19] = 0x12; 94 | command[20] = 0x0A; 95 | command[21] = 0x10; 96 | // 读取的数据时位 -> Data read-time bit 97 | command[22] = 0x01; 98 | // 访问数据的个数 -> Number of Access data 99 | command[23] = 0x00; 100 | command[24] = 0x01; 101 | // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block 102 | command[25] = (byte)(address.db_block / 256); 103 | command[26] = (byte)(address.db_block % 256); 104 | // 访问数据类型 -> Types of reading data 105 | command[27] = address.data_code; 106 | // 偏移位置 -> Offset position 107 | command[28] = (byte)(address.address_start / 256 / 256 % 256); 108 | command[29] = (byte)(address.address_start / 256 % 256); 109 | command[30] = (byte)(address.address_start % 256); 110 | 111 | byte_array_info ret = { 0 }; 112 | ret.data = command; 113 | ret.length = command_len; 114 | return ret; 115 | } 116 | 117 | byte_array_info build_write_byte_command(siemens_s7_address_data address, byte_array_info value) 118 | { 119 | int val_len = 0; 120 | if (value.data != NULL) 121 | val_len = value.length; 122 | 123 | const ushort command_len = 35 + val_len; 124 | byte* command = (byte*)malloc(command_len); 125 | build_command_header(command, command_len, 0x05); 126 | 127 | // 写入长度+4 -> Write Length +4 128 | command[15] = (byte)((4 + val_len) / 256); 129 | command[16] = (byte)((4 + val_len) % 256); 130 | // 读写指令 -> Read and write instructions 131 | command[17] = 0x05; 132 | // 写入数据块个数 -> Number of data blocks written 133 | command[18] = 0x01; 134 | // 固定,返回数据长度 -> Fixed, return data length 135 | command[19] = 0x12; 136 | command[20] = 0x0A; 137 | command[21] = 0x10; 138 | if (address.data_code == 0x06 || address.data_code == 0x07) 139 | { 140 | // 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by byte, 4 is by word 141 | command[22] = 0x04; 142 | // 写入数据的个数 -> Number of Write Data 143 | command[23] = (byte)(val_len / 2 / 256); 144 | command[24] = (byte)(val_len / 2 % 256); 145 | } 146 | else 147 | { 148 | // 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word 149 | command[22] = 0x02; 150 | // 写入数据的个数 -> Number of Write Data 151 | command[23] = (byte)(val_len / 256); 152 | command[24] = (byte)(val_len % 256); 153 | } 154 | // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block 155 | command[25] = (byte)(address.db_block / 256); 156 | command[26] = (byte)(address.db_block % 256); 157 | // 写入数据的类型 -> Types of writing data 158 | command[27] = address.data_code; 159 | // 偏移位置 -> Offset position 160 | command[28] = (byte)(address.address_start / 256 / 256 % 256); ; 161 | command[29] = (byte)(address.address_start / 256 % 256); 162 | command[30] = (byte)(address.address_start % 256); 163 | // 按字写入 -> Write by Word 164 | command[31] = 0x00; 165 | command[32] = 0x04; 166 | // 按位计算的长度 -> The length of the bitwise calculation 167 | command[33] = (byte)(val_len * 8 / 256); 168 | command[34] = (byte)(val_len * 8 % 256); 169 | 170 | if (value.data != NULL) 171 | { 172 | memcpy(command + 35, value.data, val_len); 173 | } 174 | 175 | byte_array_info ret = { 0 }; 176 | ret.data = command; 177 | ret.length = command_len; 178 | return ret; 179 | } 180 | 181 | byte_array_info build_write_bit_command(siemens_s7_address_data address, bool value) 182 | { 183 | byte buffer[1] = { 0 }; 184 | ushort buffer_len = sizeof(buffer); 185 | buffer[0] = value ? (byte)0x01 : (byte)0x00; 186 | 187 | const ushort command_len = 35 + buffer_len; 188 | byte* command = (byte*)malloc(command_len); 189 | build_command_header(command, command_len, 0x05); 190 | 191 | // 写入长度+4 -> Write Length +4 192 | command[15] = (byte)((4 + buffer_len) / 256); 193 | command[16] = (byte)((4 + buffer_len) % 256); 194 | // 命令起始符 -> Command start character 195 | command[17] = 0x05; 196 | // 写入数据块个数 -> Number of data blocks written 197 | command[18] = 0x01; 198 | command[19] = 0x12; 199 | command[20] = 0x0A; 200 | command[21] = 0x10; 201 | // 写入方式,1是按位,2是按字 -> Write mode, 1 is bitwise, 2 is by word 202 | command[22] = 0x01; 203 | // 写入数据的个数 -> Number of Write Data 204 | command[23] = (byte)(buffer_len / 256); 205 | command[24] = (byte)(buffer_len % 256); 206 | // DB块编号,如果访问的是DB块的话 -> DB block number, if you are accessing a DB block 207 | command[25] = (byte)(address.db_block / 256); 208 | command[26] = (byte)(address.db_block % 256); 209 | // 写入数据的类型 -> Types of writing data 210 | command[27] = address.data_code; 211 | // 偏移位置 -> Offset position 212 | command[28] = (byte)(address.address_start / 256 / 256); 213 | command[29] = (byte)(address.address_start / 256); 214 | command[30] = (byte)(address.address_start % 256); 215 | // 按位写入 -> Bitwise Write 216 | if (address.data_code == 0x1C) 217 | { 218 | command[31] = 0x00; 219 | command[32] = 0x09; 220 | } 221 | else 222 | { 223 | command[31] = 0x00; 224 | command[32] = 0x03; 225 | } 226 | // 按位计算的长度 -> The length of the bitwise calculation 227 | command[33] = (byte)(buffer_len / 256); 228 | command[34] = (byte)(buffer_len % 256); 229 | 230 | memcpy(command + 35, buffer, buffer_len); 231 | 232 | byte_array_info ret = { 0 }; 233 | ret.data = command; 234 | ret.length = command_len; 235 | return ret; 236 | } 237 | 238 | /// 239 | /// 读取BOOL时,根据S7协议的返回报文,正确提取出实际的数据内容 240 | /// 241 | /// 242 | /// 243 | /// 244 | s7_error_code_e s7_analysis_read_bit(byte_array_info response, byte_array_info* ret) 245 | { 246 | s7_error_code_e ret_code = S7_ERROR_CODE_SUCCESS; 247 | if (response.length == 0) 248 | return S7_ERROR_CODE_FAILED; 249 | 250 | if (response.length >= MIN_HEADER_SIZE && response.data[20] == 1) 251 | { 252 | byte buffer[1] = { 0 }; 253 | if (22 < response.length) 254 | { 255 | if (response.data[21] == 0xFF && response.data[22] == 0x03) 256 | { 257 | buffer[0] = response.data[25]; 258 | } 259 | else if (response.data[21] == 0x05 && response.data[22] == 0x00) 260 | { 261 | ret_code = S7_ERROR_CODE_READ_LENGTH_OVER_PLC_ASSIGN; 262 | } 263 | else if (response.data[21] == 0x06 && response.data[22] == 0x00) 264 | { 265 | ret_code = S7_ERROR_CODE_ERROR_0006; 266 | } 267 | else if (response.data[21] == 0x0A && response.data[22] == 0x00) 268 | { 269 | ret_code = S7_ERROR_CODE_ERROR_000A; 270 | } 271 | else 272 | { 273 | ret_code = S7_ERROR_CODE_UNKOWN; 274 | } 275 | } 276 | 277 | ret->data = (byte*)malloc(1); 278 | memset(ret->data, 0, 1); 279 | memcpy(ret->data, buffer, 1); 280 | ret->length = 1; 281 | } 282 | else 283 | { 284 | ret_code = S7_ERROR_CODE_DATA_LENGTH_CHECK_FAILED; 285 | } 286 | 287 | return ret_code; 288 | } 289 | 290 | s7_error_code_e s7_analysis_read_byte(byte_array_info response, byte_array_info* ret) 291 | { 292 | s7_error_code_e ret_code = S7_ERROR_CODE_SUCCESS; 293 | if (response.length == 0) 294 | return S7_ERROR_CODE_FAILED; 295 | 296 | int i = 0, j = 0; 297 | byte buffer[1024] = { 0 }; 298 | int buffer_length = 0; 299 | int temp_index = 0; 300 | if (response.length >= MIN_HEADER_SIZE) 301 | { 302 | for (i = 21; i < response.length - 1; i++) 303 | { 304 | if (response.data[i] == 0xFF && response.data[i + 1] == 0x04) 305 | { 306 | int count = (response.data[i + 2] * 256 + response.data[i + 3]) / 8; 307 | memcpy(buffer + buffer_length, response.data + i + 4, count); 308 | buffer_length += count; 309 | 310 | i += count + 3; 311 | } 312 | else if (response.data[i] == 0xFF && response.data[i + 1] == 0x09) 313 | { 314 | int count = response.data[i + 2] * 256 + response.data[i + 3]; 315 | if (count % 3 == 0) 316 | { 317 | for (j = 0; j < count / 3; j++) 318 | { 319 | temp_index = i + 5 + 3 * j; 320 | memcpy(buffer + buffer_length, (void*)(response.data + temp_index), 2); 321 | buffer_length += 2; 322 | } 323 | } 324 | else 325 | { 326 | for (j = 0; j < count / 5; j++) 327 | { 328 | temp_index = i + 7 + 5 * j; 329 | memcpy(buffer + buffer_length, (void*)(response.data + temp_index), 2); 330 | buffer_length += 2; 331 | } 332 | } 333 | i += count + 4; 334 | } 335 | else if (response.data[i] == 0x05 && response.data[i + 1] == 0x00) 336 | ret_code = S7_ERROR_CODE_READ_LENGTH_OVER_PLC_ASSIGN; 337 | else if (response.data[i] == 0x06 && response.data[i + 1] == 0x00) 338 | ret_code = S7_ERROR_CODE_ERROR_0006; 339 | else if (response.data[i] == 0x0A && response.data[i + 1] == 0x00) 340 | ret_code = S7_ERROR_CODE_ERROR_000A; 341 | } 342 | 343 | ret->data = (byte*)malloc(buffer_length); 344 | memset(ret->data, 0, buffer_length); 345 | memcpy(ret->data, buffer, buffer_length); 346 | ret->length = buffer_length; 347 | } 348 | else 349 | { 350 | ret_code = S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 351 | } 352 | return ret_code; 353 | } 354 | 355 | s7_error_code_e s7_analysis_write(byte_array_info response) 356 | { 357 | s7_error_code_e ret_code = S7_ERROR_CODE_SUCCESS; 358 | if (response.length == 0) 359 | return S7_ERROR_CODE_FAILED; 360 | 361 | byte buffer[1024] = { 0 }; 362 | int buffer_length = 0; 363 | if (response.length > 21) 364 | { 365 | byte code = response.data[21]; 366 | if (code != 0xFF) 367 | ret_code = S7_ERROR_CODE_WRITE_ERROR; 368 | } 369 | else 370 | { 371 | ret_code = S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 372 | } 373 | return ret_code; 374 | } 375 | 376 | bool read_data_from_core_server(int fd, byte_array_info send, byte_array_info* ret) 377 | { 378 | bool is_ok = false; 379 | int need_send = send.length; 380 | int real_sends = socket_send_data(fd, send.data, need_send); 381 | if (real_sends == need_send) 382 | { 383 | byte temp[BUFFER_SIZE]; 384 | memset(temp, 0, BUFFER_SIZE); 385 | 386 | int recv_size = socket_recv_data_one_loop(fd, temp, BUFFER_SIZE); 387 | int min = 21; 388 | if (recv_size >= min) //header size 389 | { 390 | ret->data = (byte*)malloc(recv_size); 391 | memset(ret->data, 0, recv_size); 392 | memcpy(ret->data, temp, recv_size); 393 | ret->length = recv_size; 394 | 395 | is_ok = true; 396 | } 397 | } 398 | return is_ok; 399 | } 400 | 401 | bool send_data_to_core_server(int fd, byte_array_info send) 402 | { 403 | bool is_ok = false; 404 | int need_send = send.length; 405 | int real_sends = socket_send_data(fd, send.data, need_send); 406 | if (real_sends == need_send) 407 | { 408 | is_ok = true; 409 | } 410 | return is_ok; 411 | } 412 | 413 | bool try_send_data_to_server(int fd, byte_array_info* in_bytes, int* real_sends) 414 | { 415 | if (fd < 0 || in_bytes == NULL) 416 | return false; 417 | 418 | int temp_real_sends = 0; 419 | if (real_sends == NULL) 420 | real_sends = &temp_real_sends; 421 | 422 | int retry_times = 0; 423 | while (retry_times < MAX_RETRY_TIMES) { 424 | int need_send = in_bytes->length; 425 | *real_sends = socket_send_data(fd, in_bytes->data, need_send); 426 | if (*real_sends == need_send) { 427 | break; 428 | } 429 | // 处理发送失败的逻辑,例如重试或记录错误 430 | retry_times++; 431 | } 432 | 433 | if (retry_times >= MAX_RETRY_TIMES) { 434 | return false; 435 | } 436 | 437 | return true; 438 | } -------------------------------------------------------------------------------- /siemens_plc_s7_net/siemens_s7.c: -------------------------------------------------------------------------------- 1 | #include "siemens_helper.h" 2 | #include "siemens_s7.h" 3 | #include "siemens_s7_private.h" 4 | 5 | #include "socket.h" 6 | #include 7 | #include 8 | #include 9 | #ifdef _WIN32 10 | #include 11 | #include 12 | #include 13 | #pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ 14 | #pragma warning(disable:4996) 15 | #else 16 | #include 17 | #include 18 | #include 19 | #endif 20 | 21 | siemens_plc_types_e current_plc = S1200; 22 | ushort word_length = 2; 23 | int port = 102; 24 | char ip_address[64] = { 0 }; 25 | 26 | void s7_initialization(siemens_plc_types_e plc, char* ip) 27 | { 28 | word_length = 2; 29 | strcpy(ip_address, ip); 30 | current_plc = plc; 31 | 32 | switch (plc) 33 | { 34 | case S1200: 35 | g_plc_head1[21] = 0; 36 | break; 37 | 38 | case S300: 39 | g_plc_head1[21] = 2; 40 | break; 41 | 42 | case S400: 43 | g_plc_head1[21] = 3; 44 | g_plc_head1[17] = 0x00; 45 | break; 46 | 47 | case S1500: 48 | g_plc_head1[21] = 0; 49 | break; 50 | 51 | case S200Smart: 52 | memcpy(g_plc_head1, g_plc_head1_200smart, sizeof(g_plc_head1)); 53 | memcpy(g_plc_head2, g_plc_head2_200smart, sizeof(g_plc_head2)); 54 | break; 55 | 56 | case S200: 57 | memcpy(g_plc_head1, g_plc_head1_200, sizeof(g_plc_head1)); 58 | memcpy(g_plc_head2, g_plc_head2_200, sizeof(g_plc_head2)); 59 | break; 60 | 61 | default: 62 | g_plc_head1[18] = 0; 63 | break; 64 | } 65 | } 66 | 67 | bool s7_connect(char* ip_addr, int port, siemens_plc_types_e plc, int* fd) 68 | { 69 | bool ret = false; 70 | int temp_fd = -1; 71 | temp_fd = socket_open_tcp_client_socket(ip_addr, port); 72 | s7_initialization(plc, ip_addr); 73 | *fd = temp_fd; 74 | 75 | if (temp_fd > 0) 76 | ret = initialization_on_connect(temp_fd); 77 | 78 | if (!ret && temp_fd > 0) 79 | { 80 | socket_close_tcp_socket(temp_fd); 81 | *fd = -1; 82 | } 83 | return ret; 84 | } 85 | 86 | bool s7_disconnect(int fd) 87 | { 88 | socket_close_tcp_socket(fd); 89 | return true; 90 | } 91 | 92 | ////////////////////////////////////////////////////////////////////////// 93 | s7_error_code_e s7_read_response(int fd, byte_array_info* response, int* read_count) 94 | { 95 | if (fd <= 0 || read_count == 0 || response == NULL) 96 | return S7_ERROR_CODE_INVALID_PARAMETER; 97 | 98 | byte* temp = malloc(BUFFER_SIZE); // 动态分配缓冲区 99 | if (temp == NULL) 100 | return S7_ERROR_CODE_MALLOC_FAILED; 101 | 102 | memset(temp, 0, BUFFER_SIZE); 103 | response->data = temp; 104 | response->length = BUFFER_SIZE; 105 | 106 | *read_count = 0; 107 | char* ptr = (char*)response->data; 108 | 109 | if (fd <= 0 || response->length <= 0) return -1; 110 | *read_count = (int)recv(fd, ptr, response->length, 0); 111 | 112 | if (*read_count < 0) { 113 | return S7_ERROR_CODE_FAILED; 114 | } 115 | response->length = *read_count; 116 | 117 | return S7_ERROR_CODE_SUCCESS; 118 | } 119 | 120 | s7_error_code_e s7_read_data(int fd, const char* address, int length, byte_array_info* out_bytes, bool is_bit) 121 | { 122 | if (fd <= 0 || address == NULL || length <= 0 || out_bytes == NULL) 123 | return S7_ERROR_CODE_INVALID_PARAMETER; 124 | 125 | siemens_s7_address_data address_data; 126 | if (!s7_analysis_address(address, length, &address_data)) 127 | return S7_ERROR_CODE_PARSE_ADDRESS_FAILED; 128 | 129 | s7_error_code_e ret = S7_ERROR_CODE_UNKOWN; 130 | byte_array_info core_cmd = is_bit ? build_read_bit_command(address_data) : build_read_byte_command(address_data); 131 | if (core_cmd.data == NULL) 132 | return S7_ERROR_CODE_BUILD_CORE_CMD_FAILED; 133 | 134 | if (!try_send_data_to_server(fd, &core_cmd, NULL)) { 135 | RELEASE_DATA(core_cmd.data); 136 | return S7_ERROR_CODE_SOCKET_SEND_FAILED; 137 | } 138 | RELEASE_DATA(core_cmd.data); 139 | 140 | byte_array_info response = { 0 }; 141 | int recv_size = 0; 142 | ret = s7_read_response(fd, &response, &recv_size); 143 | if (ret != S7_ERROR_CODE_SUCCESS) 144 | { 145 | RELEASE_DATA(response.data); 146 | return ret; 147 | } 148 | 149 | if (recv_size < MIN_HEADER_SIZE) { 150 | RELEASE_DATA(response.data); 151 | return S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 152 | } 153 | 154 | ret = is_bit ? s7_analysis_read_bit(response, out_bytes) : s7_analysis_read_byte(response, out_bytes); 155 | RELEASE_DATA(response.data); 156 | 157 | return ret; 158 | } 159 | 160 | s7_error_code_e read_bit_value(int fd, const char* address, int length, byte_array_info* out_bytes) 161 | { 162 | return s7_read_data(fd, address, length, out_bytes, true); 163 | } 164 | 165 | s7_error_code_e read_byte_value(int fd, const char* address, int length, byte_array_info* out_bytes) 166 | { 167 | return s7_read_data(fd, address, length, out_bytes, false); 168 | } 169 | 170 | s7_error_code_e s7_write_data(int fd, const char* address, int length, byte_array_info in_bytes, bool is_bit, bool value) 171 | { 172 | if (fd <= 0 || address == NULL || length <= 0) 173 | return S7_ERROR_CODE_INVALID_PARAMETER; 174 | 175 | siemens_s7_address_data address_data; 176 | if (!s7_analysis_address(address, length, &address_data)) 177 | return S7_ERROR_CODE_PARSE_ADDRESS_FAILED; 178 | 179 | s7_error_code_e ret = S7_ERROR_CODE_UNKOWN; 180 | byte_array_info core_cmd = is_bit ? build_write_bit_command(address_data, value) : build_write_byte_command(address_data, in_bytes); 181 | if (core_cmd.data == NULL) 182 | return S7_ERROR_CODE_BUILD_CORE_CMD_FAILED; 183 | 184 | if (!try_send_data_to_server(fd, &core_cmd, NULL)) { 185 | RELEASE_DATA(core_cmd.data); 186 | return S7_ERROR_CODE_SOCKET_SEND_FAILED; 187 | } 188 | RELEASE_DATA(core_cmd.data); 189 | 190 | byte_array_info response = { 0 }; 191 | int recv_size = 0; 192 | ret = s7_read_response(fd, &response, &recv_size); 193 | if (ret != S7_ERROR_CODE_SUCCESS) 194 | { 195 | RELEASE_DATA(response.data); 196 | return ret; 197 | } 198 | 199 | if (recv_size < MIN_HEADER_SIZE) { 200 | RELEASE_DATA(response.data); 201 | return S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 202 | } 203 | 204 | ret = s7_analysis_write(response); 205 | RELEASE_DATA(response.data); 206 | 207 | return ret; 208 | } 209 | 210 | s7_error_code_e write_bit_value(int fd, const char* address, int length, bool value) 211 | { 212 | byte_array_info dummy = { 0 }; 213 | return s7_write_data(fd, address, length, dummy, true, value); 214 | } 215 | 216 | s7_error_code_e write_byte_value(int fd, const char* address, int length, byte_array_info in_bytes) 217 | { 218 | return s7_write_data(fd, address, length, in_bytes, false, false); 219 | } 220 | 221 | s7_error_code_e s7_remote_run(int fd) 222 | { 223 | if (fd < 0) 224 | return S7_ERROR_CODE_INVALID_PARAMETER; 225 | 226 | s7_error_code_e ret = S7_ERROR_CODE_UNKOWN; 227 | const byte* core_cmd_temp = g_s7_stop; 228 | 229 | int core_cmd_len = sizeof(core_cmd_temp); 230 | byte* core_cmd = (byte*)malloc(core_cmd_len); 231 | if (core_cmd == NULL) 232 | return S7_ERROR_CODE_BUILD_CORE_CMD_FAILED; 233 | 234 | memcpy(core_cmd, core_cmd_temp, core_cmd_len); 235 | 236 | byte_array_info temp = { 0 }; 237 | temp.data = core_cmd; 238 | temp.length = core_cmd_len; 239 | 240 | if (!try_send_data_to_server(fd, &temp, NULL)) { 241 | RELEASE_DATA(temp.data); 242 | return S7_ERROR_CODE_SOCKET_SEND_FAILED; 243 | } 244 | RELEASE_DATA(temp.data); 245 | 246 | byte_array_info response = { 0 }; 247 | int recv_size = 0; 248 | ret = s7_read_response(fd, &response, &recv_size); 249 | if (ret != S7_ERROR_CODE_SUCCESS) 250 | { 251 | RELEASE_DATA(response.data); 252 | return false; 253 | } 254 | 255 | if (recv_size < MIN_HEADER_SIZE) { 256 | RELEASE_DATA(response.data); 257 | return S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 258 | } 259 | 260 | ret = s7_analysis_write(response); 261 | RELEASE_DATA(response.data); 262 | 263 | return ret; 264 | } 265 | 266 | s7_error_code_e s7_remote_stop(int fd) 267 | { 268 | if (fd <= 0) 269 | return S7_ERROR_CODE_INVALID_PARAMETER; 270 | 271 | s7_error_code_e ret = S7_ERROR_CODE_UNKOWN; 272 | const byte* core_cmd_temp = g_s7_stop; 273 | int core_cmd_len = sizeof(g_s7_stop); 274 | 275 | byte* core_cmd = (byte*)malloc(core_cmd_len); 276 | if (core_cmd == NULL) 277 | return S7_ERROR_CODE_BUILD_CORE_CMD_FAILED; 278 | memcpy(core_cmd, core_cmd_temp, core_cmd_len); 279 | 280 | byte_array_info temp = { 0 }; 281 | temp.data = core_cmd; 282 | temp.length = core_cmd_len; 283 | 284 | if (!try_send_data_to_server(fd, &temp, NULL)) { 285 | RELEASE_DATA(temp.data); 286 | return S7_ERROR_CODE_SOCKET_SEND_FAILED; 287 | } 288 | RELEASE_DATA(temp.data); 289 | 290 | byte_array_info response = { 0 }; 291 | int recv_size = 0; 292 | ret = s7_read_response(fd, &response, &recv_size); 293 | if (ret != S7_ERROR_CODE_SUCCESS) 294 | { 295 | RELEASE_DATA(response.data); 296 | return ret; 297 | } 298 | 299 | if (recv_size < MIN_HEADER_SIZE) { 300 | RELEASE_DATA(response.data); 301 | return S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 302 | } 303 | 304 | ret = s7_analysis_write(response); 305 | RELEASE_DATA(response.data); 306 | 307 | return ret; 308 | } 309 | 310 | s7_error_code_e s7_remote_reset(int fd) 311 | { 312 | if (fd <= 0) 313 | return S7_ERROR_CODE_INVALID_PARAMETER; 314 | 315 | s7_error_code_e ret = S7_ERROR_CODE_UNKOWN; 316 | byte core_cmd_temp[] = { 0x06, 0x10, 0x00, 0x00, 0x01, 0x00 }; 317 | 318 | int core_cmd_len = sizeof(core_cmd_temp) / sizeof(core_cmd_temp[0]); 319 | byte* core_cmd = (byte*)malloc(core_cmd_len); 320 | memcpy(core_cmd, core_cmd_temp, core_cmd_len); 321 | 322 | byte_array_info temp = { 0 }; 323 | temp.data = core_cmd; 324 | temp.length = core_cmd_len; 325 | 326 | if (!try_send_data_to_server(fd, &temp, NULL)) { 327 | RELEASE_DATA(temp.data); 328 | return S7_ERROR_CODE_SOCKET_SEND_FAILED; 329 | } 330 | RELEASE_DATA(temp.data); 331 | 332 | byte_array_info response = { 0 }; 333 | int recv_size = 0; 334 | ret = s7_read_response(fd, &response, &recv_size); 335 | if (ret != S7_ERROR_CODE_SUCCESS) 336 | { 337 | RELEASE_DATA(response.data); 338 | return ret; 339 | } 340 | 341 | if (recv_size < MIN_HEADER_SIZE) { 342 | RELEASE_DATA(response.data); 343 | return S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 344 | } 345 | 346 | ret = s7_analysis_write(response); 347 | RELEASE_DATA(response.data); 348 | 349 | return ret; 350 | } 351 | 352 | s7_error_code_e s7_read_plc_type(int fd, char** type) 353 | { 354 | if (fd <= 0) 355 | return S7_ERROR_CODE_INVALID_PARAMETER; 356 | 357 | s7_error_code_e ret = S7_ERROR_CODE_UNKOWN; 358 | byte_array_info out_bytes = { 0 }; 359 | 360 | byte* core_cmd = g_plc_order_number; 361 | int core_cmd_len = sizeof(g_plc_order_number); 362 | 363 | byte_array_info temp = { 0 }; 364 | temp.data = core_cmd; 365 | temp.length = core_cmd_len; 366 | 367 | if (!try_send_data_to_server(fd, &temp, NULL)) { 368 | return S7_ERROR_CODE_SOCKET_SEND_FAILED; 369 | } 370 | 371 | byte_array_info response = { 0 }; 372 | int recv_size = 0; 373 | ret = s7_read_response(fd, &response, &recv_size); 374 | if (ret != S7_ERROR_CODE_SUCCESS) 375 | { 376 | RELEASE_DATA(response.data); 377 | return ret; 378 | } 379 | 380 | if (recv_size < MIN_HEADER_SIZE) { 381 | RELEASE_DATA(response.data); 382 | return S7_ERROR_CODE_RESPONSE_HEADER_FAILED; 383 | } 384 | 385 | out_bytes.length = 20; 386 | out_bytes.data = (byte*)malloc(out_bytes.length + 1); 387 | memset(out_bytes.data, 0, out_bytes.length + 1); 388 | memcpy((char*)out_bytes.data, response.data + 71, out_bytes.length); 389 | if (out_bytes.length > 0) 390 | { 391 | *type = (char*)out_bytes.data; 392 | } 393 | 394 | return ret; 395 | } 396 | 397 | bool initialization_on_connect(int fd) 398 | { 399 | byte_array_info ret; 400 | bool is_ok = false; 401 | 402 | // 第一次握手 -> First handshake 403 | byte_array_info temp = { 0 }; 404 | temp.data = g_plc_head1; 405 | temp.length = sizeof(g_plc_head1); 406 | is_ok = read_data_from_core_server(fd, temp, &ret); 407 | if (!is_ok) 408 | return false; 409 | else 410 | if (NULL != ret.data) free(ret.data); 411 | 412 | // 第二次握手 -> Second handshake 413 | temp.data = g_plc_head2; 414 | temp.length = sizeof(g_plc_head2); 415 | is_ok = read_data_from_core_server(fd, temp, &ret); 416 | if (!is_ok) 417 | return false; 418 | 419 | // 调整单次接收的pdu长度信息 420 | g_pdu_length = ntohs(bytes2ushort(ret.data + ret.length - 2)) - 28; 421 | if (g_pdu_length < 200) g_pdu_length = 200; 422 | 423 | if (NULL != ret.data) free(ret.data); 424 | 425 | // 返回成功的信号 -> Return a successful signal 426 | return true; 427 | } 428 | 429 | ////////////////////////////////////////////////////////////////////////// 430 | 431 | s7_error_code_e s7_read_bool(int fd, const char* address, bool* val) 432 | { 433 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 434 | return S7_ERROR_CODE_INVALID_PARAMETER; 435 | 436 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 437 | byte_array_info read_data = { 0 }; 438 | ret = read_bit_value(fd, address, 1, &read_data); 439 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length > 0) 440 | { 441 | *val = (bool)read_data.data[0]; 442 | RELEASE_DATA(read_data.data); 443 | } 444 | return ret; 445 | } 446 | 447 | s7_error_code_e s7_read_byte(int fd, const char* address, byte* val) 448 | { 449 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 450 | return S7_ERROR_CODE_INVALID_PARAMETER; 451 | 452 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 453 | byte_array_info read_data = { 0 }; 454 | ret = read_byte_value(fd, address, 1, &read_data); 455 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length > 0) 456 | { 457 | *val = read_data.data[0]; 458 | RELEASE_DATA(read_data.data); 459 | } 460 | return ret; 461 | } 462 | 463 | s7_error_code_e s7_read_short(int fd, const char* address, short* val) 464 | { 465 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 466 | return S7_ERROR_CODE_INVALID_PARAMETER; 467 | 468 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 469 | byte_array_info read_data = { 0 }; 470 | ret = read_byte_value(fd, address, 2, &read_data); 471 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length > 0) 472 | { 473 | *val = (short)ntohs(bytes2short(read_data.data)); 474 | RELEASE_DATA(read_data.data); 475 | } 476 | return ret; 477 | } 478 | 479 | s7_error_code_e s7_read_ushort(int fd, const char* address, ushort* val) 480 | { 481 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 482 | return S7_ERROR_CODE_INVALID_PARAMETER; 483 | 484 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 485 | byte_array_info read_data = { 0 }; 486 | ret = read_byte_value(fd, address, 2, &read_data); 487 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= 2) 488 | { 489 | *val = ntohs(bytes2ushort(read_data.data)); 490 | RELEASE_DATA(read_data.data); 491 | } 492 | return ret; 493 | } 494 | 495 | s7_error_code_e s7_read_int32(int fd, const char* address, int32* val) 496 | { 497 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 498 | return S7_ERROR_CODE_INVALID_PARAMETER; 499 | 500 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 501 | byte_array_info read_data = { 0 }; 502 | ret = read_byte_value(fd, address, 4, &read_data); 503 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= 4) 504 | { 505 | *val = (int32)ntohl(bytes2int32(read_data.data)); 506 | RELEASE_DATA(read_data.data); 507 | } 508 | return ret; 509 | } 510 | 511 | s7_error_code_e s7_read_uint32(int fd, const char* address, uint32* val) 512 | { 513 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 514 | return S7_ERROR_CODE_INVALID_PARAMETER; 515 | 516 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 517 | byte_array_info read_data = { 0 }; 518 | ret = read_byte_value(fd, address, 4, &read_data); 519 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= 2) 520 | { 521 | *val = ntohl(bytes2uint32(read_data.data)); 522 | RELEASE_DATA(read_data.data); 523 | } 524 | return ret; 525 | } 526 | 527 | s7_error_code_e s7_read_int64(int fd, const char* address, int64* val) 528 | { 529 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 530 | return S7_ERROR_CODE_INVALID_PARAMETER; 531 | 532 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 533 | byte_array_info read_data = { 0 }; 534 | ret = read_byte_value(fd, address, 8, &read_data); 535 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= 8) 536 | { 537 | *val = (int64)ntohll_(bytes2bigInt(read_data.data)); 538 | RELEASE_DATA(read_data.data); 539 | } 540 | return ret; 541 | } 542 | 543 | s7_error_code_e s7_read_uint64(int fd, const char* address, uint64* val) 544 | { 545 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 546 | return S7_ERROR_CODE_INVALID_PARAMETER; 547 | 548 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 549 | byte_array_info read_data = { 0 }; 550 | ret = read_byte_value(fd, address, 8, &read_data); 551 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= 8) 552 | { 553 | *val = ntohll_(bytes2ubigInt(read_data.data)); 554 | RELEASE_DATA(read_data.data); 555 | } 556 | return ret; 557 | } 558 | 559 | s7_error_code_e s7_read_float(int fd, const char* address, float* val) 560 | { 561 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 562 | return S7_ERROR_CODE_INVALID_PARAMETER; 563 | 564 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 565 | byte_array_info read_data = { 0 }; 566 | ret = read_byte_value(fd, address, 4, &read_data); 567 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= 4) 568 | { 569 | *val = ntohf_(bytes2uint32(read_data.data)); 570 | RELEASE_DATA(read_data.data); 571 | } 572 | return ret; 573 | } 574 | 575 | s7_error_code_e s7_read_double(int fd, const char* address, double* val) 576 | { 577 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 578 | return S7_ERROR_CODE_INVALID_PARAMETER; 579 | 580 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 581 | byte_array_info read_data = { 0 }; 582 | ret = read_byte_value(fd, address, 8, &read_data); 583 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= 8) 584 | { 585 | *val = ntohd_(bytes2ubigInt(read_data.data)); 586 | RELEASE_DATA(read_data.data); 587 | } 588 | return ret; 589 | } 590 | 591 | s7_error_code_e s7_read_string(int fd, const char* address, int length, char** val) 592 | { 593 | if (fd <= 0 || address == NULL || length <= 0 || strlen(address) == 0) 594 | return S7_ERROR_CODE_INVALID_PARAMETER; 595 | 596 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 597 | byte_array_info read_data = { 0 }; 598 | int read_len = (length % 2) == 1 ? length + 1 : length; 599 | ret = read_byte_value(fd, address, length, &read_data); 600 | if (ret == S7_ERROR_CODE_SUCCESS && read_data.length >= read_len) 601 | { 602 | char* ret_str = (char*)malloc(read_len); 603 | memset(ret_str, 0, read_len); 604 | memcpy(ret_str, read_data.data, read_len); 605 | RELEASE_DATA(read_data.data); 606 | *val = ret_str; 607 | } 608 | return ret; 609 | } 610 | 611 | s7_error_code_e s7_write_bool(int fd, const char* address, bool val) 612 | { 613 | if (fd <= 0 || address == NULL || strlen(address) == 0) 614 | return S7_ERROR_CODE_INVALID_PARAMETER; 615 | 616 | return write_bit_value(fd, address, 1, val); 617 | } 618 | 619 | s7_error_code_e s7_write_byte(int fd, const char* address, byte val) 620 | { 621 | if (fd <= 0 || address == NULL || strlen(address) == 0) 622 | return S7_ERROR_CODE_INVALID_PARAMETER; 623 | 624 | int write_len = 1; 625 | char temp[1] = { 0 }; 626 | byte_array_info write_data = { 0 }; 627 | write_data.data = temp; 628 | write_data.data[0] = val; 629 | write_data.length = write_len; 630 | 631 | return write_byte_value(fd, address, 1, write_data); 632 | } 633 | 634 | s7_error_code_e s7_write_short(int fd, const char* address, short val) 635 | { 636 | if (fd <= 0 || address == NULL || strlen(address) == 0) 637 | return S7_ERROR_CODE_INVALID_PARAMETER; 638 | 639 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 640 | int write_len = 2; 641 | byte_array_info write_data = { 0 }; 642 | write_data.data = (byte*)malloc(write_len); 643 | memset(write_data.data, 0, write_len); 644 | write_data.length = write_len; 645 | 646 | short2bytes(htons(val), write_data.data); 647 | ret = write_byte_value(fd, address, 2, write_data); 648 | RELEASE_DATA(write_data.data); 649 | return ret; 650 | } 651 | 652 | s7_error_code_e s7_write_ushort(int fd, const char* address, ushort val) 653 | { 654 | if (fd <= 0 || address == NULL || strlen(address) == 0) 655 | return S7_ERROR_CODE_INVALID_PARAMETER; 656 | 657 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 658 | int write_len = 2; 659 | byte_array_info write_data = { 0 }; 660 | write_data.data = (byte*)malloc(write_len); 661 | memset(write_data.data, 0, write_len); 662 | write_data.length = write_len; 663 | 664 | ushort2bytes(htons(val), write_data.data); 665 | ret = write_byte_value(fd, address, 2, write_data); 666 | RELEASE_DATA(write_data.data); 667 | return ret; 668 | } 669 | 670 | s7_error_code_e s7_write_int32(int fd, const char* address, int32 val) 671 | { 672 | if (fd <= 0 || address == NULL || strlen(address) == 0) 673 | return S7_ERROR_CODE_INVALID_PARAMETER; 674 | 675 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 676 | int write_len = 4; 677 | byte_array_info write_data = { 0 }; 678 | write_data.data = (byte*)malloc(write_len); 679 | memset(write_data.data, 0, write_len); 680 | write_data.length = write_len; 681 | 682 | int2bytes(htonl(val), write_data.data); 683 | ret = write_byte_value(fd, address, 4, write_data); 684 | RELEASE_DATA(write_data.data); 685 | return ret; 686 | } 687 | 688 | s7_error_code_e s7_write_uint32(int fd, const char* address, uint32 val) 689 | { 690 | if (fd <= 0 || address == NULL || strlen(address) == 0) 691 | return S7_ERROR_CODE_INVALID_PARAMETER; 692 | 693 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 694 | int write_len = 4; 695 | byte_array_info write_data = { 0 }; 696 | write_data.data = (byte*)malloc(write_len); 697 | memset(write_data.data, 0, write_len); 698 | write_data.length = write_len; 699 | 700 | uint2bytes(htonl(val), write_data.data); 701 | ret = write_byte_value(fd, address, 4, write_data); 702 | RELEASE_DATA(write_data.data); 703 | return ret; 704 | } 705 | 706 | s7_error_code_e s7_write_int64(int fd, const char* address, int64 val) 707 | { 708 | if (fd <= 0 || address == NULL || strlen(address) == 0) 709 | return S7_ERROR_CODE_INVALID_PARAMETER; 710 | 711 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 712 | int write_len = 8; 713 | byte_array_info write_data = { 0 }; 714 | write_data.data = (byte*)malloc(write_len); 715 | memset(write_data.data, 0, write_len); 716 | write_data.length = write_len; 717 | 718 | bigInt2bytes(htonll_(val), write_data.data); 719 | ret = write_byte_value(fd, address, 8, write_data); 720 | RELEASE_DATA(write_data.data); 721 | return ret; 722 | } 723 | 724 | s7_error_code_e s7_write_uint64(int fd, const char* address, uint64 val) 725 | { 726 | if (fd <= 0 || address == NULL || strlen(address) == 0) 727 | return S7_ERROR_CODE_INVALID_PARAMETER; 728 | 729 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 730 | int write_len = 8; 731 | byte_array_info write_data = { 0 }; 732 | write_data.data = (byte*)malloc(write_len); 733 | memset(write_data.data, 0, write_len); 734 | write_data.length = write_len; 735 | 736 | ubigInt2bytes(htonll_(val), write_data.data); 737 | ret = write_byte_value(fd, address, 8, write_data); 738 | RELEASE_DATA(write_data.data); 739 | return ret; 740 | } 741 | 742 | s7_error_code_e s7_write_float(int fd, const char* address, float val) 743 | { 744 | if (fd <= 0 || address == NULL || strlen(address) == 0) 745 | return S7_ERROR_CODE_INVALID_PARAMETER; 746 | 747 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 748 | int write_len = 4; 749 | byte_array_info write_data = { 0 }; 750 | write_data.data = (byte*)malloc(write_len); 751 | memset(write_data.data, 0, write_len); 752 | write_data.length = write_len; 753 | 754 | uint2bytes(htonf_(val), write_data.data); 755 | ret = write_byte_value(fd, address, 4, write_data); 756 | RELEASE_DATA(write_data.data); 757 | return ret; 758 | } 759 | 760 | s7_error_code_e s7_write_double(int fd, const char* address, double val) 761 | { 762 | if (fd <= 0 || address == NULL || strlen(address) == 0) 763 | return S7_ERROR_CODE_INVALID_PARAMETER; 764 | 765 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 766 | int write_len = 8; 767 | byte_array_info write_data = { 0 }; 768 | write_data.data = (byte*)malloc(write_len); 769 | memset(write_data.data, 0, write_len); 770 | write_data.length = write_len; 771 | 772 | bigInt2bytes(htond_(val), write_data.data); 773 | ret = write_byte_value(fd, address, 8, write_data); 774 | RELEASE_DATA(write_data.data); 775 | return ret; 776 | } 777 | 778 | s7_error_code_e s7_write_string(int fd, const char* address, int length, const char* val) 779 | { 780 | if (fd <= 0 || address == NULL || strlen(address) == 0 || val == NULL) 781 | return S7_ERROR_CODE_INVALID_PARAMETER; 782 | 783 | s7_error_code_e ret = S7_ERROR_CODE_FAILED; 784 | int write_len = (length % 2) == 1 ? length + 1 : length; 785 | byte_array_info write_data = { 0 }; 786 | write_data.data = (byte*)malloc(write_len); 787 | memset(write_data.data, 0, write_len); 788 | memcpy(write_data.data, val, length); 789 | write_data.length = write_len; 790 | 791 | ret = write_byte_value(fd, address, write_len, write_data); 792 | RELEASE_DATA(write_data.data); 793 | return ret; 794 | } 795 | 796 | byte get_plc_slot() 797 | { 798 | return g_plc_slot; 799 | } 800 | 801 | void set_plc_slot(byte slot) 802 | { 803 | g_plc_slot = slot; 804 | if (current_plc != S200 && current_plc != S200Smart) 805 | g_plc_head1[21] = (byte)((g_plc_rack * 0x20) + g_plc_slot); 806 | } 807 | 808 | byte get_plc_rack() 809 | { 810 | return g_plc_rack; 811 | } 812 | 813 | void set_plc_rack(byte rack) 814 | { 815 | g_plc_rack = rack; 816 | if (current_plc != S200 && current_plc != S200Smart) 817 | g_plc_head1[21] = (byte)((g_plc_rack * 0x20) + g_plc_slot); 818 | } 819 | 820 | byte get_plc_connection_type() 821 | { 822 | return g_plc_head1[20]; 823 | } 824 | 825 | void set_plc_connection_type(byte type) 826 | { 827 | if (current_plc != S200 && current_plc != S200Smart) 828 | g_plc_head1[20] = type; 829 | } 830 | 831 | int get_plc_local_TSAP() 832 | { 833 | if (current_plc == S200 || current_plc == S200Smart) 834 | return g_plc_head1[13] * 256 + g_plc_head1[14]; 835 | else 836 | return g_plc_head1[16] * 256 + g_plc_head1[17]; 837 | } 838 | 839 | void set_plc_local_TSAP(int tasp) 840 | { 841 | byte temp[4] = { 0 }; 842 | int2bytes(tasp, temp); 843 | 844 | if (current_plc == S200 || current_plc == S200Smart) 845 | { 846 | g_plc_head1[13] = temp[1]; 847 | g_plc_head1[14] = temp[0]; 848 | } 849 | else 850 | { 851 | g_plc_head1[16] = temp[1]; 852 | g_plc_head1[17] = temp[0]; 853 | } 854 | } 855 | 856 | int get_plc_dest_TSAP() 857 | { 858 | if (current_plc == S200 || current_plc == S200Smart) 859 | return g_plc_head1[17] * 256 + g_plc_head1[18]; 860 | else 861 | return g_plc_head1[20] * 256 + g_plc_head1[21]; 862 | } 863 | 864 | void set_plc_dest_TSAP(int tasp) 865 | { 866 | byte temp[4] = { 0 }; 867 | int2bytes(tasp, temp); 868 | 869 | if (current_plc == S200 || current_plc == S200Smart) 870 | { 871 | g_plc_head1[17] = temp[1]; 872 | g_plc_head1[18] = temp[0]; 873 | } 874 | else 875 | { 876 | g_plc_head1[20] = temp[1]; 877 | g_plc_head1[21] = temp[0]; 878 | } 879 | } 880 | 881 | int get_plc_PDU_length() 882 | { 883 | return g_pdu_length; 884 | } -------------------------------------------------------------------------------- /siemens_plc_s7_net/dynstr.c: -------------------------------------------------------------------------------- 1 | /* A C dynamic strings library. 2 | * 3 | * Copyright (c) 2006-2014, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "dynstr.h" 40 | 41 | #define DYNSTR_MAX_PREALLOC (1024*1024) 42 | 43 | #ifndef container_of 44 | #define container_of(ptr, type, member) \ 45 | ((type *) ((char *) (ptr) - offsetof(type, member))) 46 | #endif 47 | 48 | size_t dynstr_len(const dynstr s) 49 | { 50 | assert(s != NULL); 51 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 52 | return sh->len; 53 | } 54 | 55 | size_t dynstr_avail(const dynstr s) 56 | { 57 | assert(s != NULL); 58 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 59 | return sh->free; 60 | } 61 | 62 | /* Create a new dynstr string with the content specified by the 'init' pointer 63 | * and 'initlen'. 64 | * If NULL is used for 'init' the string is initialized with zero bytes. 65 | * 66 | * The string is always null-termined (all the dynstr strings are, always) so 67 | * even if you create an dynstr string with: 68 | * 69 | * mystring = dynstr_newlen("abc",3"); 70 | * 71 | * You can print the string with printf() as there is an implicit \0 at the 72 | * end of the string. However the string is binary safe and can contain 73 | * \0 characters in the middle, as the length is stored in the dynstr header. 74 | **/ 75 | dynstr dynstr_newlen(const void* init, size_t initlen) 76 | { 77 | struct dynstr_hdr* sh; 78 | 79 | if (init) { 80 | sh = malloc(sizeof(struct dynstr_hdr) + initlen + 1); 81 | } 82 | else { 83 | sh = calloc(sizeof(struct dynstr_hdr) + initlen + 1, 1); 84 | } 85 | if (sh == NULL) return NULL; 86 | sh->len = initlen; 87 | sh->free = 0; 88 | if (initlen && init) 89 | memcpy(sh->buf, init, initlen); 90 | sh->buf[initlen] = '\0'; 91 | return (char*)sh->buf; 92 | } 93 | 94 | /* Create an empty (zero length) dynstr string. Even in this case the string 95 | * always has an implicit null term. 96 | **/ 97 | dynstr dynstr_empty(void) 98 | { 99 | return dynstr_newlen("", 0); 100 | } 101 | 102 | /* Create a new dynstr string starting from a null termined C string. 103 | **/ 104 | dynstr dynstr_new(const char* init) 105 | { 106 | size_t initlen = (init == NULL) ? 0 : strlen(init); 107 | return dynstr_newlen(init, initlen); 108 | } 109 | 110 | /* Duplicate an dynstr string. */ 111 | dynstr dynstr_dup(const dynstr s) 112 | { 113 | return dynstr_newlen(s, dynstr_len(s)); 114 | } 115 | 116 | /* Free an dynstr string. No operation is performed if 's' is NULL. 117 | **/ 118 | void dynstr_free(dynstr s) 119 | { 120 | if (s == NULL) 121 | return; 122 | free(container_of(s, struct dynstr_hdr, buf)); 123 | } 124 | 125 | /* Set the dynstr string length to the length as obtained with strlen(), so 126 | * considering as content only up to the first null term character. 127 | * 128 | * This function is useful when the dynstr string is hacked manually in some 129 | * way, like in the following example: 130 | * 131 | * s = dynstr_new("foobar"); 132 | * s[2] = '\0'; 133 | * dynstr_updatelen(s); 134 | * printf("%d\n", dynstr_len(s)); 135 | * 136 | * The output will be "2", but if we comment out the call to dynstr_updatelen() 137 | * the output will be "6" as the string was modified but the logical length 138 | * remains 6 bytes. 139 | **/ 140 | void dynstr_updatelen(dynstr s) 141 | { 142 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 143 | int reallen = strlen(s); 144 | sh->free += (sh->len - reallen); 145 | sh->len = reallen; 146 | } 147 | 148 | /* Modify an dynstr string on-place to make it empty (zero length). 149 | * However all the existing buffer is not discarded but set as free space 150 | * so that next append operations will not require allocations up to the 151 | * number of bytes previously available. 152 | **/ 153 | void dynstr_clear(dynstr s) 154 | { 155 | assert(s != NULL); 156 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 157 | sh->free += sh->len; 158 | sh->len = 0; 159 | sh->buf[0] = '\0'; 160 | } 161 | 162 | /* Enlarge the free space at the end of the dynstr string so that the caller 163 | * is sure that after calling this function can overwrite up to addlen 164 | * bytes after the end of the string, plus one more byte for nul term. 165 | * 166 | * Note: this does not change the *length* of the dynstr string as returned 167 | * by dynstr_len(), but only the free buffer space we have. 168 | **/ 169 | dynstr dynstrMakeRoomFor(dynstr s, size_t addlen) 170 | { 171 | struct dynstr_hdr* sh, * newsh; 172 | size_t free = dynstr_avail(s); 173 | size_t len, newlen; 174 | 175 | if (free >= addlen) return s; 176 | len = dynstr_len(s); 177 | sh = container_of(s, struct dynstr_hdr, buf); 178 | newlen = (len + addlen); 179 | if (newlen < DYNSTR_MAX_PREALLOC) 180 | newlen *= 2; 181 | else 182 | newlen += DYNSTR_MAX_PREALLOC; 183 | newsh = realloc(sh, sizeof(struct dynstr_hdr) + newlen + 1); 184 | if (newsh == NULL) return NULL; 185 | 186 | newsh->free = newlen - len; 187 | return newsh->buf; 188 | } 189 | 190 | /* Reallocate the dynstr string so that it has no free space at the end. The 191 | * contained string remains not altered, but next concatenation operations 192 | * will require a reallocation. 193 | * 194 | * After the call, the passed dynstr string is no longer valid and all the 195 | * references must be substituted with the new pointer returned by the call. 196 | **/ 197 | dynstr dynstrRemoveFreeSpace(dynstr s) 198 | { 199 | struct dynstr_hdr* sh; 200 | assert(s != NULL); 201 | sh = container_of(s, struct dynstr_hdr, buf); 202 | sh = realloc(sh, sizeof(struct dynstr_hdr) + sh->len + 1); 203 | sh->free = 0; 204 | return sh->buf; 205 | } 206 | 207 | /* Return the total size of the allocation of the specifed dynstr string, 208 | * including: 209 | * 1) The dynstr header before the pointer. 210 | * 2) The string. 211 | * 3) The free buffer at the end if any. 212 | * 4) The implicit null term. 213 | **/ 214 | size_t dynstrAllocSize(dynstr s) 215 | { 216 | assert(s != NULL); 217 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 218 | return sizeof(struct dynstr_hdr) + sh->len + sh->free + 1; 219 | } 220 | 221 | /* Increment the dynstr length and decrements the left free space at the 222 | * end of the string according to 'incr'. Also set the null term 223 | * in the new end of the string. 224 | * 225 | * This function is used in order to fix the string length after the 226 | * user calls dynstrMakeRoomFor(), writes something after the end of 227 | * the current string, and finally needs to set the new length. 228 | * 229 | * Note: it is possible to use a negative increment in order to 230 | * right-trim the string. 231 | * 232 | * Usage example: 233 | * 234 | * Using dynstrIncrLen() and dynstrMakeRoomFor() it is possible to mount the 235 | * following schema, to cat bytes coming from the kernel to the end of an 236 | * dynstr string without copying into an intermediate buffer: 237 | * 238 | * oldlen = dynstr_len(s); 239 | * s = dynstrMakeRoomFor(s, BUFFER_SIZE); 240 | * nread = read(fd, s+oldlen, BUFFER_SIZE); 241 | * ... check for nread <= 0 and handle it ... 242 | * dynstrIncrLen(s, nread); 243 | **/ 244 | void dynstrIncrLen(dynstr s, int incr) 245 | { 246 | assert(s != NULL); 247 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 248 | assert(sh->free >= incr); 249 | sh->len += incr; 250 | sh->free -= incr; 251 | assert(sh->free >= 0); 252 | s[sh->len] = '\0'; 253 | } 254 | 255 | /* Grow the dynstr to have the specified length. Bytes that were not part of 256 | * the original length of the dynstr will be set to zero. 257 | * 258 | * if the specified length is smaller than the current length, no operation 259 | * is performed. 260 | **/ 261 | dynstr dynstr_growzero(dynstr s, size_t len) 262 | { 263 | assert(s != NULL); 264 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 265 | size_t totlen, curlen = sh->len; 266 | 267 | if (len <= curlen) return s; 268 | s = dynstrMakeRoomFor(s, len - curlen); 269 | if (s == NULL) 270 | return NULL; 271 | 272 | /* Make sure added region doesn't contain garbage */ 273 | sh = container_of(s, struct dynstr_hdr, buf); 274 | memset(s + curlen, 0, (len - curlen + 1)); /* also set trailing \0 byte */ 275 | totlen = sh->len + sh->free; 276 | sh->len = len; 277 | sh->free = totlen - sh->len; 278 | return s; 279 | } 280 | 281 | /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the 282 | * end of the specified dynstr string 's'. 283 | * 284 | * After the call, the passed dynstr string is no longer valid and all the 285 | * references must be substituted with the new pointer returned by the call. 286 | **/ 287 | dynstr dynstr_catlen(dynstr s, const void* t, size_t len) 288 | { 289 | struct dynstr_hdr* sh; 290 | size_t curlen = dynstr_len(s); 291 | 292 | s = dynstrMakeRoomFor(s, len); 293 | if (s == NULL) return NULL; 294 | sh = container_of(s, struct dynstr_hdr, buf); 295 | memcpy(s + curlen, t, len); 296 | sh->len = curlen + len; 297 | sh->free = sh->free - len; 298 | s[curlen + len] = '\0'; 299 | return s; 300 | } 301 | 302 | /* Append the specified null termianted C string to the dynstr string 's'. 303 | * 304 | * After the call, the passed dynstr string is no longer valid and all the 305 | * references must be substituted with the new pointer returned by the call. 306 | **/ 307 | dynstr dynstr_cat(dynstr s, const char* t) 308 | { 309 | return dynstr_catlen(s, t, strlen(t)); 310 | } 311 | 312 | /* Append the specified dynstr 't' to the existing dynstr 's'. 313 | * 314 | * After the call, the modified dynstr string is no longer valid and all the 315 | * references must be substituted with the new pointer returned by the call. 316 | **/ 317 | dynstr dynstr_cat_dynstr(dynstr s, const dynstr t) 318 | { 319 | return dynstr_catlen(s, t, dynstr_len(t)); 320 | } 321 | 322 | /* Destructively modify the dynstr string 's' to hold the specified binary 323 | * safe string pointed by 't' of length 'len' bytes. 324 | **/ 325 | dynstr dynstr_cpylen(dynstr s, const char* t, size_t len) 326 | { 327 | assert(s != NULL); 328 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 329 | size_t totlen = sh->free + sh->len; 330 | 331 | if (totlen < len) { 332 | s = dynstrMakeRoomFor(s, len - sh->len); 333 | if (s == NULL) return NULL; 334 | sh = container_of(s, struct dynstr_hdr, buf); 335 | totlen = sh->free + sh->len; 336 | } 337 | memcpy(s, t, len); 338 | s[len] = '\0'; 339 | sh->len = len; 340 | sh->free = totlen - len; 341 | return s; 342 | } 343 | 344 | /* Like dynstr_cpylen() but 't' must be a null-termined string so that the length 345 | * of the string is obtained with strlen(). 346 | **/ 347 | dynstr dynstr_cpy(dynstr s, const char* t) 348 | { 349 | return dynstr_cpylen(s, t, strlen(t)); 350 | } 351 | 352 | dynstr dynstr_vsprintf(const char* fmt, va_list ap) 353 | { 354 | va_list cpy; 355 | dynstr s; 356 | int len; 357 | 358 | va_copy(cpy, ap); 359 | len = vsnprintf(NULL, 0, fmt, cpy); 360 | s = dynstr_newlen(0, len); 361 | if (s == NULL) return NULL; 362 | dynstr_clear(s); 363 | va_copy(cpy, ap); 364 | if (vsnprintf(s, dynstr_avail(s), fmt, cpy) == len) { 365 | dynstrIncrLen(s, len); 366 | return s; 367 | } 368 | dynstr_free(s); 369 | return NULL; 370 | } 371 | 372 | dynstr dynstr_sprintf(const char* fmt, ...) 373 | { 374 | dynstr s; 375 | va_list ap; 376 | va_start(ap, fmt); 377 | s = dynstr_vsprintf(fmt, ap); 378 | va_end(ap); 379 | return s; 380 | } 381 | 382 | /* Like dynstr_catpritf() but gets va_list instead of being variadic. 383 | **/ 384 | dynstr dynstr_catvprintf(dynstr s, const char* fmt, va_list ap) 385 | { 386 | va_list cpy; 387 | int len; 388 | 389 | va_copy(cpy, ap); 390 | len = vsnprintf(NULL, 0, fmt, cpy); 391 | if (len <= 0) return s; 392 | s = dynstrMakeRoomFor(s, len); 393 | va_copy(cpy, ap); 394 | if (vsnprintf(s + dynstr_len(s), dynstr_avail(s), fmt, cpy) == len) { 395 | dynstrIncrLen(s, len); 396 | return s; 397 | } 398 | return s; 399 | } 400 | 401 | /* Append to the dynstr string 's' a string obtained using printf-alike format 402 | * specifier. 403 | * 404 | * After the call, the modified dynstr string is no longer valid and all the 405 | * references must be substituted with the new pointer returned by the call. 406 | * 407 | * Example: 408 | * 409 | * s = dynstr_new("Sum is: "); 410 | * s = dynstr_catprintf(s, "%d+%d = %d", a, b, a + b). 411 | * 412 | * Often you need to create a string from scratch with the printf-alike 413 | * format. When this is the need, just use dynstr_empty() as the target string: 414 | * 415 | * s = dynstr_catprintf(dynstr_empty(), "... your format ...", args); 416 | **/ 417 | dynstr dynstr_catprintf(dynstr s, const char* fmt, ...) 418 | { 419 | va_list ap; 420 | char* t; 421 | va_start(ap, fmt); 422 | t = dynstr_catvprintf(s, fmt, ap); 423 | va_end(ap); 424 | return t; 425 | } 426 | 427 | /* Remove the part of the string from left and from right composed just of 428 | * contiguous characters found in 'cset', that is a null terminted C string. 429 | * 430 | * After the call, the modified dynstr string is no longer valid and all the 431 | * references must be substituted with the new pointer returned by the call. 432 | * 433 | * Example: 434 | * 435 | * s = dynstr_new("AA...AA.a.aa.aHelloWorld :::"); 436 | * s = dynstr_trim(s, "A. :"); 437 | * printf("%s\n", s); 438 | * 439 | * Output will be just "HelloWorld". 440 | **/ 441 | void dynstr_trim(dynstr s, const char* cset) 442 | { 443 | assert(s != NULL); 444 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 445 | char* start, * end, * sp, * ep; 446 | size_t len; 447 | 448 | sp = start = s; 449 | ep = end = s + dynstr_len(s) - 1; 450 | while (sp <= end && strchr(cset, *sp)) sp++; 451 | while (ep > start && strchr(cset, *ep)) ep--; 452 | len = (sp > ep) ? 0 : ((ep - sp) + 1); 453 | if (sh->buf != sp) memmove(sh->buf, sp, len); 454 | sh->buf[len] = '\0'; 455 | sh->free = sh->free + (sh->len - len); 456 | sh->len = len; 457 | } 458 | 459 | /* Turn the string into a smaller (or equal) string containing only the 460 | * substring specified by the 'start' and 'end' indexes. 461 | * 462 | * start and end can be negative, where -1 means the last character of the 463 | * string, -2 the penultimate character, and so forth. 464 | * 465 | * The interval is inclusive, so the start and end characters will be part 466 | * of the resulting string. 467 | * 468 | * The string is modified in-place. 469 | * 470 | * Example: 471 | * 472 | * s = dynstr_new("Hello World"); 473 | * dynstr_range(s, 1, -1); => "ello World" 474 | **/ 475 | void dynstr_range(dynstr s, int start, int end) 476 | { 477 | struct dynstr_hdr* sh = container_of(s, struct dynstr_hdr, buf); 478 | size_t newlen, len = dynstr_len(s); 479 | 480 | if (len == 0) return; 481 | if (start < 0) { 482 | start = len + start; 483 | if (start < 0) start = 0; 484 | } 485 | if (end < 0) { 486 | end = len + end; 487 | if (end < 0) end = 0; 488 | } 489 | newlen = (start > end) ? 0 : (end - start) + 1; 490 | if (newlen != 0) { 491 | if (start >= (signed)len) { 492 | newlen = 0; 493 | } 494 | else if (end >= (signed)len) { 495 | end = len - 1; 496 | newlen = (start > end) ? 0 : (end - start) + 1; 497 | } 498 | } 499 | else { 500 | start = 0; 501 | } 502 | if (start && newlen) 503 | memmove(sh->buf, sh->buf + start, newlen); 504 | sh->buf[newlen] = 0; 505 | sh->free = sh->free + (sh->len - newlen); 506 | sh->len = newlen; 507 | } 508 | 509 | /* Apply tolower() to every character of the dynstr string 's'. 510 | **/ 511 | void dynstr_tolower(dynstr s) 512 | { 513 | int len = dynstr_len(s), j; 514 | 515 | for (j = 0; j < len; j++) s[j] = tolower(s[j]); 516 | } 517 | 518 | /* Apply toupper() to every character of the dynstr string 's'. 519 | **/ 520 | void dynstr_toupper(dynstr s) 521 | { 522 | int len = dynstr_len(s), j; 523 | 524 | for (j = 0; j < len; j++) s[j] = toupper(s[j]); 525 | } 526 | 527 | /* Compare two dynstr strings s1 and s2 with memcmp(). 528 | * 529 | * Return value: 530 | * 531 | * 1 if s1 > s2. 532 | * -1 if s1 < s2. 533 | * 0 if s1 and s2 are exactly the same binary string. 534 | * 535 | * If two strings share exactly the same prefix, but one of the two has 536 | * additional characters, the longer string is considered to be greater than 537 | * the smaller one. 538 | **/ 539 | int dynstr_cmp(const dynstr s1, const dynstr s2) 540 | { 541 | size_t l1, l2, minlen; 542 | int cmp; 543 | 544 | l1 = dynstr_len(s1); 545 | l2 = dynstr_len(s2); 546 | minlen = (l1 < l2) ? l1 : l2; 547 | cmp = memcmp(s1, s2, minlen); 548 | if (cmp == 0) return l1 - l2; 549 | return cmp; 550 | } 551 | 552 | /* Create an dynstr string from a long long value. It is much faster than: 553 | * 554 | * dynstr_catprintf(dynstr_empty(), "%lld\n", value); 555 | **/ 556 | dynstr dynstr_fromlonglong(long long value) 557 | { 558 | char buf[32], * p; 559 | unsigned long long v; 560 | 561 | v = (value < 0) ? -value : value; 562 | p = buf + 31; /* point to the last character */ 563 | do { 564 | *p-- = '0' + (v % 10); 565 | v /= 10; 566 | } while (v); 567 | if (value < 0) *p-- = '-'; 568 | p++; 569 | return dynstr_newlen(p, 32 - (p - buf)); 570 | } 571 | 572 | /* Append to the dynstr string "s" an escaped string representation where 573 | * all the non-printable characters (tested with isprint()) are turned into 574 | * escapes in the form "\n\r\a...." or "\x". 575 | * 576 | * After the call, the modified dynstr string is no longer valid and all the 577 | * references must be substituted with the new pointer returned by the call. 578 | **/ 579 | dynstr dynstr_catrepr(dynstr s, const char* p, size_t len) 580 | { 581 | s = dynstr_catlen(s, "\"", 1); 582 | while (len--) { 583 | switch (*p) { 584 | case '\\': 585 | case '"': 586 | s = dynstr_catprintf(s, "\\%c", *p); 587 | break; 588 | case '\n': s = dynstr_catlen(s, "\\n", 2); break; 589 | case '\r': s = dynstr_catlen(s, "\\r", 2); break; 590 | case '\t': s = dynstr_catlen(s, "\\t", 2); break; 591 | case '\a': s = dynstr_catlen(s, "\\a", 2); break; 592 | case '\b': s = dynstr_catlen(s, "\\b", 2); break; 593 | default: 594 | if (isprint(*p)) 595 | s = dynstr_catprintf(s, "%c", *p); 596 | else 597 | s = dynstr_catprintf(s, "\\x%02x", (unsigned char)*p); 598 | break; 599 | } 600 | p++; 601 | } 602 | return dynstr_catlen(s, "\"", 1); 603 | } 604 | 605 | /* Helper function for dynstr_splitargs() that returns non zero if 'c' 606 | * is a valid hex digit. 607 | **/ 608 | int is_hex_digit(char c) 609 | { 610 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 611 | (c >= 'A' && c <= 'F'); 612 | } 613 | 614 | /* Helper function for dynstr_splitargs() that converts a hex digit into an 615 | * integer from 0 to 15 616 | **/ 617 | int hex_digit_to_int(char c) 618 | { 619 | switch (c) { 620 | case '0': return 0; 621 | case '1': return 1; 622 | case '2': return 2; 623 | case '3': return 3; 624 | case '4': return 4; 625 | case '5': return 5; 626 | case '6': return 6; 627 | case '7': return 7; 628 | case '8': return 8; 629 | case '9': return 9; 630 | case 'a': case 'A': return 10; 631 | case 'b': case 'B': return 11; 632 | case 'c': case 'C': return 12; 633 | case 'd': case 'D': return 13; 634 | case 'e': case 'E': return 14; 635 | case 'f': case 'F': return 15; 636 | default: return 0; 637 | } 638 | } 639 | 640 | /* Split a line into arguments, where every argument can be in the 641 | * following programming-language REPL-alike form: 642 | * 643 | * foo bar "newline are supported\n" and "\xff\x00otherstuff" 644 | * 645 | * The number of arguments is stored into *argc, and an array 646 | * of dynstr is returned. 647 | * 648 | * The caller should free the resulting array of dynstr strings with 649 | * dynstr_freesplitres(). 650 | * 651 | * Note that dynstr_catrepr() is able to convert back a string into 652 | * a quoted string in the same format dynstr_splitargs() is able to parse. 653 | * 654 | * The function returns the allocated tokens on success, even when the 655 | * input string is empty, or NULL if the input contains unbalanced 656 | * quotes or closed quotes followed by non space characters 657 | * as in: "foo"bar or "foo' 658 | **/ 659 | dynstr* dynstr_splitargs(const char* line, int* argc) 660 | { 661 | const char* p = line; 662 | char* current = NULL; 663 | char** vector = NULL; 664 | 665 | *argc = 0; 666 | while (1) { 667 | /* skip blanks */ 668 | while (*p && isspace(*p)) p++; 669 | if (*p) { 670 | /* get a token */ 671 | int inq = 0; /* set to 1 if we are in "quotes" */ 672 | int insq = 0; /* set to 1 if we are in 'single quotes' */ 673 | int done = 0; 674 | 675 | if (current == NULL) current = dynstr_empty(); 676 | while (!done) { 677 | if (inq) { 678 | if (*p == '\\' && *(p + 1) == 'x' && 679 | is_hex_digit(*(p + 2)) && 680 | is_hex_digit(*(p + 3))) { 681 | unsigned char byte; 682 | 683 | byte = (hex_digit_to_int(*(p + 2)) * 16) + 684 | hex_digit_to_int(*(p + 3)); 685 | current = dynstr_catlen(current, (char*)&byte, 1); 686 | p += 3; 687 | } 688 | else if (*p == '\\' && *(p + 1)) { 689 | char c; 690 | 691 | p++; 692 | switch (*p) { 693 | case 'n': c = '\n'; break; 694 | case 'r': c = '\r'; break; 695 | case 't': c = '\t'; break; 696 | case 'b': c = '\b'; break; 697 | case 'a': c = '\a'; break; 698 | default: c = *p; break; 699 | } 700 | current = dynstr_catlen(current, &c, 1); 701 | } 702 | else if (*p == '"') { 703 | /* closing quote must be followed by a space or 704 | * nothing at all. 705 | **/ 706 | if (*(p + 1) && !isspace(*(p + 1))) goto err; 707 | done = 1; 708 | } 709 | else if (!*p) { 710 | /* unterminated quotes */ 711 | goto err; 712 | } 713 | else { 714 | current = dynstr_catlen(current, p, 1); 715 | } 716 | } 717 | else if (insq) { 718 | if (*p == '\\' && *(p + 1) == '\'') { 719 | p++; 720 | current = dynstr_catlen(current, "'", 1); 721 | } 722 | else if (*p == '\'') { 723 | /* closing quote must be followed by a space or 724 | * nothing at all. */ 725 | if (*(p + 1) && !isspace(*(p + 1))) goto err; 726 | done = 1; 727 | } 728 | else if (!*p) { 729 | /* unterminated quotes */ 730 | goto err; 731 | } 732 | else { 733 | current = dynstr_catlen(current, p, 1); 734 | } 735 | } 736 | else { 737 | switch (*p) { 738 | case ' ': 739 | case '\n': 740 | case '\r': 741 | case '\t': 742 | case '\0': 743 | done = 1; 744 | break; 745 | case '"': 746 | inq = 1; 747 | break; 748 | case '\'': 749 | insq = 1; 750 | break; 751 | default: 752 | current = dynstr_catlen(current, p, 1); 753 | break; 754 | } 755 | } 756 | if (*p) p++; 757 | } 758 | /* add the token to the vector */ 759 | vector = realloc(vector, ((*argc) + 1) * sizeof(char*)); 760 | vector[*argc] = current; 761 | (*argc)++; 762 | current = NULL; 763 | } 764 | else { 765 | /* Even on empty input string return something not NULL. */ 766 | if (vector == NULL) vector = malloc(sizeof(void*)); 767 | return vector; 768 | } 769 | } 770 | 771 | err: 772 | while ((*argc)--) 773 | dynstr_free(vector[*argc]); 774 | free(vector); 775 | if (current) dynstr_free(current); 776 | *argc = 0; 777 | return NULL; 778 | } 779 | 780 | /* Modify the string substituting all the occurrences of the set of 781 | * characters specified in the 'from' string to the corresponding character 782 | * in the 'to' array. 783 | * 784 | * For instance: dynstr_mapchars(mystring, "ho", "01", 2) 785 | * will have the effect of turning the string "hello" into "0ell1". 786 | * 787 | * The function returns the dynstr string pointer, that is always the same 788 | * as the input pointer since no resize is needed. 789 | **/ 790 | dynstr dynstr_mapchars(dynstr s, const char* from, const char* to, size_t setlen) 791 | { 792 | size_t j, i, l = dynstr_len(s); 793 | 794 | for (j = 0; j < l; j++) { 795 | for (i = 0; i < setlen; i++) { 796 | if (s[j] == from[i]) { 797 | s[j] = to[i]; 798 | break; 799 | } 800 | } 801 | } 802 | return s; 803 | } 804 | 805 | /* Join an array of C strings using the specified separator (also a C string). 806 | * Returns the result as an dynstr string. 807 | **/ 808 | dynstr dynstr_join(char** argv, int argc, char* sep, size_t seplen) 809 | { 810 | dynstr join = dynstr_empty(); 811 | int j; 812 | 813 | for (j = 0; j < argc; j++) { 814 | join = dynstr_cat(join, argv[j]); 815 | if (j != argc - 1) join = dynstr_catlen(join, sep, seplen); 816 | } 817 | return join; 818 | } 819 | 820 | /* Like dynstr_join, but joins an array of dynstr strings. 821 | **/ 822 | dynstr dynstr_join_dynstr(dynstr* argv, int argc, const char* sep, size_t seplen) 823 | { 824 | dynstr join = dynstr_empty(); 825 | int j; 826 | 827 | for (j = 0; j < argc; j++) { 828 | join = dynstr_cat_dynstr(join, argv[j]); 829 | if (j != argc - 1) join = dynstr_catlen(join, sep, seplen); 830 | } 831 | return join; 832 | } 833 | 834 | #define STRING_BLOOM_ADD(mask, ch) ((mask |= (1UL << ((ch) & (sizeof(mask) * 8 - 1))))) 835 | #define STRING_BLOOM(mask, ch) ((mask & (1UL << ((ch) & (sizeof(mask) * 8 -1))))) 836 | 837 | enum { 838 | FAST_COUNT, 839 | FAST_SEARCH, 840 | FAST_RSEARCH, 841 | }; 842 | 843 | static int fastsearch(const char* s, int n, 844 | const char* p, int m, int maxcount, int mode) 845 | { 846 | unsigned long mask; 847 | int skip, count = 0; 848 | int i, j, mlast, w; 849 | 850 | w = n - m; 851 | 852 | if (w < 0 || (mode == FAST_COUNT && maxcount == 0)) 853 | return -1; 854 | 855 | /* look for special cases */ 856 | if (m <= 1) { 857 | if (m <= 0) 858 | return -1; 859 | /* use special case for 1-character strings */ 860 | if (mode == FAST_COUNT) { 861 | for (i = 0; i < n; i++) 862 | if (s[i] == p[0]) { 863 | count++; 864 | if (count == maxcount) 865 | return maxcount; 866 | } 867 | return count; 868 | } 869 | else if (mode == FAST_SEARCH) { 870 | for (i = 0; i < n; i++) 871 | if (s[i] == p[0]) 872 | return i; 873 | } 874 | else { /* FAST_RSEARCH */ 875 | for (i = n - 1; i > -1; i--) 876 | if (s[i] == p[0]) 877 | return i; 878 | } 879 | return -1; 880 | } 881 | 882 | mlast = m - 1; 883 | skip = mlast - 1; 884 | mask = 0; 885 | 886 | if (mode != FAST_RSEARCH) { 887 | /* create compressed boyer-moore delta 1 table */ 888 | 889 | /* process pattern[:-1] */ 890 | for (i = 0; i < mlast; i++) { 891 | STRING_BLOOM_ADD(mask, p[i]); 892 | if (p[i] == p[mlast]) 893 | skip = mlast - i - 1; 894 | } 895 | /* process pattern[-1] outside the loop */ 896 | STRING_BLOOM_ADD(mask, p[mlast]); 897 | 898 | for (i = 0; i <= w; i++) { 899 | /* note: using mlast in the skip path slows things down on x86 */ 900 | if (s[i + m - 1] == p[m - 1]) { 901 | /* candidate match */ 902 | for (j = 0; j < mlast; j++) 903 | if (s[i + j] != p[j]) 904 | break; 905 | if (j == mlast) { 906 | /* got a match! */ 907 | if (mode != FAST_COUNT) 908 | return i; 909 | count++; 910 | if (count == maxcount) 911 | return maxcount; 912 | i = i + mlast; 913 | continue; 914 | } 915 | /* miss: check if next character is part of pattern */ 916 | if (!STRING_BLOOM(mask, s[i + m])) 917 | i = i + m; 918 | else 919 | i = i + skip; 920 | } 921 | else { 922 | /* skip: check if next character is part of pattern */ 923 | if (!STRING_BLOOM(mask, s[i + m])) 924 | i = i + m; 925 | } 926 | } 927 | } 928 | else { /* FAST_RSEARCH */ 929 | 930 | /* create compressed boyer-moore delta 1 table */ 931 | 932 | /* process pattern[0] outside the loop */ 933 | STRING_BLOOM_ADD(mask, p[0]); 934 | /* process pattern[:0:-1] */ 935 | for (i = mlast; i > 0; i--) { 936 | STRING_BLOOM_ADD(mask, p[i]); 937 | if (p[i] == p[0]) 938 | skip = i - 1; 939 | } 940 | 941 | for (i = w; i >= 0; i--) { 942 | if (s[i] == p[0]) { 943 | /* candidate match */ 944 | for (j = mlast; j > 0; j--) 945 | if (s[i + j] != p[j]) 946 | break; 947 | if (j == 0) 948 | /* got a match! */ 949 | return i; 950 | /* miss: check if previous character is part of pattern */ 951 | if (i > 0 && !STRING_BLOOM(mask, s[i - 1])) 952 | i = i - m; 953 | else 954 | i = i - skip; 955 | } 956 | else { 957 | /* skip: check if previous character is part of pattern */ 958 | if (i > 0 && !STRING_BLOOM(mask, s[i - 1])) 959 | i = i - m; 960 | } 961 | } 962 | } 963 | 964 | if (mode != FAST_COUNT) 965 | return -1; 966 | return count; 967 | } 968 | 969 | /* find first substr `p` of `s`. 970 | **/ 971 | int dynstr_find(const char* s, int len, const char* p, int m) 972 | { 973 | if (len <= 0) return -1; 974 | return fastsearch(s, len, p, m, -1, FAST_SEARCH); 975 | } 976 | 977 | /* find first substr `p` of `s` from right. 978 | **/ 979 | int dynstr_rfind(const char* s, int len, const char* p, int m) 980 | { 981 | if (len <= 0) return -1; 982 | return fastsearch(s, len, p, m, -1, FAST_RSEARCH); 983 | } 984 | 985 | /* split `s` with separator in whitespace character. 986 | **/ 987 | dynstr* dynstr_splitwhitespace(const char* s, int len, int* count) 988 | { 989 | int elements = 0, slots = 5, start = 0, i = 0, j = 0; 990 | dynstr* tokens; 991 | if (len < 0) return NULL; 992 | tokens = malloc(sizeof(dynstr) * slots); 993 | if (tokens == NULL) 994 | return NULL; 995 | if (len == 0) { 996 | *count = 0; 997 | return tokens; 998 | } 999 | while (1) { 1000 | while (j < len && isspace((unsigned char)s[j])) j++; 1001 | if (j == len) break; 1002 | start = j++; 1003 | while (j < len && !isspace((unsigned char)s[j])) j++; 1004 | /* make sure there is room for the next element and the final one */ 1005 | if (slots < elements + 2) { 1006 | dynstr* newtokens; 1007 | 1008 | slots *= 2; 1009 | newtokens = realloc(tokens, sizeof(dynstr) * slots); 1010 | if (newtokens == NULL) goto cleanup; 1011 | tokens = newtokens; 1012 | } 1013 | tokens[elements] = dynstr_newlen(s + start, j - start); 1014 | if (tokens[elements] == NULL) goto cleanup; 1015 | elements++; 1016 | } 1017 | *count = elements; 1018 | return tokens; 1019 | 1020 | cleanup: 1021 | for (i = 0; i < elements; i++) 1022 | dynstr_free(tokens[i]); 1023 | free(tokens); 1024 | *count = 0; 1025 | return NULL; 1026 | } 1027 | 1028 | /* split `s` with separator in whitespace character from right. 1029 | **/ 1030 | dynstr* dynstr_rsplitwhitespace(const char* s, int len, int* count) 1031 | { 1032 | int elements = 0, slots = 5, start, i, j; 1033 | dynstr* tokens; 1034 | if (len < 0) return NULL; 1035 | tokens = malloc(sizeof(dynstr) * slots); 1036 | if (tokens == NULL) 1037 | return NULL; 1038 | if (len == 0) { 1039 | *count = 0; 1040 | return tokens; 1041 | } 1042 | 1043 | j = start = len - 1; 1044 | while (1) { 1045 | while (j >= 0 && isspace((unsigned char)s[j])) j--; 1046 | if (j < 0) break; 1047 | start = j--; 1048 | while (j >= 0 && !isspace((unsigned char)s[j])) j--; 1049 | /* make sure there is room for the next element and the final one */ 1050 | if (slots < elements + 2) { 1051 | dynstr* newtokens; 1052 | 1053 | slots *= 2; 1054 | newtokens = realloc(tokens, sizeof(dynstr) * slots); 1055 | if (newtokens == NULL) goto cleanup; 1056 | tokens = newtokens; 1057 | } 1058 | tokens[elements] = dynstr_newlen(s + j + 1, start - j); 1059 | if (tokens[elements] == NULL) goto cleanup; 1060 | elements++; 1061 | } 1062 | *count = elements; 1063 | return tokens; 1064 | cleanup: 1065 | for (i = 0; i < elements; i++) 1066 | dynstr_free(tokens[i]); 1067 | free(tokens); 1068 | *count = 0; 1069 | return NULL; 1070 | } 1071 | 1072 | /* split `s` with separator in character `ch`. 1073 | **/ 1074 | dynstr* dynstr_splitchar(const char* s, int len, const char ch, int* count) 1075 | { 1076 | int elements = 0, slots = 5, start = 0, i = 0, j = 0; 1077 | dynstr* tokens; 1078 | if (len < 0) return NULL; 1079 | tokens = malloc(sizeof(dynstr) * slots); 1080 | if (tokens == NULL) 1081 | return NULL; 1082 | if (len == 0) { 1083 | *count = 0; 1084 | return tokens; 1085 | } 1086 | 1087 | for (j = 0; j < len; j++) { 1088 | if (s[j] == ch) { 1089 | /* make sure there is room for the next element and the final one */ 1090 | if (slots < elements + 2) { 1091 | dynstr* newtokens; 1092 | 1093 | slots *= 2; 1094 | newtokens = realloc(tokens, sizeof(dynstr) * slots); 1095 | if (newtokens == NULL) goto cleanup; 1096 | tokens = newtokens; 1097 | } 1098 | tokens[elements] = dynstr_newlen(s + start, j - start); 1099 | if (tokens[elements] == NULL) goto cleanup; 1100 | elements++; 1101 | start = j = j + 1; 1102 | } 1103 | } 1104 | if (start <= len) { 1105 | tokens[elements] = dynstr_newlen(s + start, len - start); 1106 | if (tokens[elements] == NULL) goto cleanup; 1107 | elements++; 1108 | } 1109 | *count = elements; 1110 | return tokens; 1111 | 1112 | cleanup: 1113 | for (i = 0; i < elements; i++) 1114 | dynstr_free(tokens[i]); 1115 | free(tokens); 1116 | *count = 0; 1117 | return NULL; 1118 | } 1119 | 1120 | /* split `s` with separator in character `ch` from right. 1121 | **/ 1122 | dynstr* dynstr_rsplitchar(const char* s, int len, const char ch, int* count) 1123 | { 1124 | int elements = 0, slots = 5, start = 0, i = 0, j = 0; 1125 | dynstr* tokens; 1126 | if (len < 0) return NULL; 1127 | tokens = malloc(sizeof(dynstr) * slots); 1128 | if (tokens == NULL) 1129 | return NULL; 1130 | if (len == 0) { 1131 | *count = 0; 1132 | return tokens; 1133 | } 1134 | 1135 | start = j = len - 1; 1136 | for (; j >= 0; j--) { 1137 | if (s[j] == ch) { 1138 | /* make sure there is room for the next element and the final one */ 1139 | if (slots < elements + 2) { 1140 | dynstr* newtokens; 1141 | 1142 | slots *= 2; 1143 | newtokens = realloc(tokens, sizeof(dynstr) * slots); 1144 | if (newtokens == NULL) goto cleanup; 1145 | tokens = newtokens; 1146 | } 1147 | tokens[elements] = dynstr_newlen(s + j + 1, start - j); 1148 | if (tokens[elements] == NULL) goto cleanup; 1149 | elements++; 1150 | start = j = j - 1; 1151 | } 1152 | } 1153 | if (start >= -1) { 1154 | tokens[elements] = dynstr_newlen(s, start + 1); 1155 | if (tokens[elements] == NULL) goto cleanup; 1156 | elements++; 1157 | } 1158 | *count = elements; 1159 | return tokens; 1160 | cleanup: 1161 | for (i = 0; i < elements; i++) 1162 | dynstr_free(tokens[i]); 1163 | free(tokens); 1164 | *count = 0; 1165 | return NULL; 1166 | } 1167 | 1168 | /* Split 's' with separator in 'sep'. An array 1169 | * of dynstr strings is returned. *count will be set 1170 | * by reference to the number of tokens returned. 1171 | * 1172 | * On out of memory, zero length string, zero length 1173 | * separator, NULL is returned. 1174 | * 1175 | * Note that 'sep' is able to split a string using 1176 | * a multi-character separator. For example 1177 | * 1178 | * dynstr_split("_-_foo_-_bar", "_-_"); will return three 1179 | * elements "", "foo" and "bar". 1180 | * 1181 | * This version of the function is binary-safe but 1182 | * requires length arguments. dynstr_split() is just the 1183 | * same function but for zero-terminated strings. 1184 | **/ 1185 | dynstr* dynstr_split(const char* s, int len, const char* sep, int seplen, int* count) 1186 | { 1187 | int elements = 0, slots = 5, start = 0, i, j; 1188 | dynstr* tokens; 1189 | 1190 | if (seplen < 1 || len < 0) return NULL; 1191 | 1192 | if (seplen == 1) 1193 | return dynstr_splitchar(s, len, sep[0], count); 1194 | 1195 | tokens = malloc(sizeof(dynstr) * slots); 1196 | if (tokens == NULL) return NULL; 1197 | 1198 | if (len == 0) { 1199 | *count = 0; 1200 | return tokens; 1201 | } 1202 | 1203 | while (1) { 1204 | j = fastsearch(s + start, len - start, sep, seplen, -1, FAST_SEARCH); 1205 | if (j < 0) break; 1206 | /* make sure there is room for the next element and the final one */ 1207 | if (slots < elements + 2) { 1208 | dynstr* newtokens; 1209 | 1210 | slots *= 2; 1211 | newtokens = realloc(tokens, sizeof(dynstr) * slots); 1212 | if (newtokens == NULL) goto cleanup; 1213 | tokens = newtokens; 1214 | } 1215 | tokens[elements] = dynstr_newlen(s + start, j); 1216 | if (tokens[elements] == NULL) goto cleanup; 1217 | elements++; 1218 | start += j + seplen; 1219 | } 1220 | 1221 | /* Add the final element. We are sure there is room in the tokens array. */ 1222 | tokens[elements] = dynstr_newlen(s + start, len - start); 1223 | if (tokens[elements] == NULL) goto cleanup; 1224 | elements++; 1225 | *count = elements; 1226 | return tokens; 1227 | 1228 | cleanup: 1229 | for (i = 0; i < elements; i++) 1230 | dynstr_free(tokens[i]); 1231 | free(tokens); 1232 | *count = 0; 1233 | return NULL; 1234 | } 1235 | 1236 | dynstr* dynstr_rsplit(const char* s, int len, const char* sep, int seplen, int* count) 1237 | { 1238 | int elements = 0, slots = 5, start = 0, i, j; 1239 | dynstr* tokens; 1240 | 1241 | if (seplen < 1 || len < 0) return NULL; 1242 | 1243 | if (seplen == 1) 1244 | return dynstr_rsplitchar(s, len, sep[0], count); 1245 | 1246 | tokens = malloc(sizeof(dynstr) * slots); 1247 | if (tokens == NULL) return NULL; 1248 | 1249 | if (len == 0) { 1250 | *count = 0; 1251 | return tokens; 1252 | } 1253 | 1254 | start = len; 1255 | while (1) { 1256 | j = fastsearch(s, start, sep, seplen, -1, FAST_RSEARCH); 1257 | if (j < 0) break; 1258 | /* make sure there is room for the next element and the final one */ 1259 | if (slots < elements + 2) { 1260 | dynstr* newtokens; 1261 | 1262 | slots *= 2; 1263 | newtokens = realloc(tokens, sizeof(dynstr) * slots); 1264 | if (newtokens == NULL) goto cleanup; 1265 | tokens = newtokens; 1266 | } 1267 | tokens[elements] = dynstr_newlen(s + j + seplen, start - j - seplen); 1268 | if (tokens[elements] == NULL) goto cleanup; 1269 | elements++; 1270 | start = j; 1271 | } 1272 | 1273 | /* Add the final element. We are sure there is room in the tokens array. */ 1274 | tokens[elements] = dynstr_newlen(s, start); 1275 | if (tokens[elements] == NULL) goto cleanup; 1276 | elements++; 1277 | *count = elements; 1278 | return tokens; 1279 | 1280 | cleanup: 1281 | for (i = 0; i < elements; i++) 1282 | dynstr_free(tokens[i]); 1283 | free(tokens); 1284 | *count = 0; 1285 | return NULL; 1286 | } 1287 | 1288 | /* split `s` with separator in `CRLF|CR|LF` line break characters. 1289 | **/ 1290 | dynstr* dynstr_splitlines(const char* s, int len, int* count, int keepends) 1291 | { 1292 | int elements = 0, slots = 5, start = 0, i, j; 1293 | dynstr* tokens; 1294 | 1295 | if (len < 0) return NULL; 1296 | 1297 | tokens = malloc(sizeof(dynstr) * slots); 1298 | if (tokens == NULL) return NULL; 1299 | 1300 | if (len == 0) { 1301 | *count = 0; 1302 | return tokens; 1303 | } 1304 | 1305 | for (start = j = 0; j < len;) { 1306 | int eol; 1307 | 1308 | /* Find a line and append it */ 1309 | while (j < len && s[j] != '\r' && s[j] != '\n') 1310 | j++; 1311 | 1312 | /* Skip the line break reading CRLF as one line break */ 1313 | eol = j; 1314 | if (j < len) { 1315 | if (s[j] == '\r' && j + 1 < len && s[j + 1] == '\n') 1316 | j += 2; 1317 | else 1318 | j++; 1319 | if (keepends) 1320 | eol = j; 1321 | } 1322 | /* make sure there is room for the next element and the final one */ 1323 | if (slots < elements + 2) { 1324 | dynstr* newtokens; 1325 | 1326 | slots *= 2; 1327 | newtokens = realloc(tokens, sizeof(dynstr) * slots); 1328 | if (newtokens == NULL) goto cleanup; 1329 | tokens = newtokens; 1330 | } 1331 | tokens[elements] = dynstr_newlen(s + start, eol - start); 1332 | if (tokens[elements] == NULL) goto cleanup; 1333 | elements++; 1334 | start = j; 1335 | } 1336 | *count = elements; 1337 | return tokens; 1338 | 1339 | cleanup: 1340 | for (i = 0; i < elements; i++) 1341 | dynstr_free(tokens[i]); 1342 | free(tokens); 1343 | *count = 0; 1344 | return NULL; 1345 | } 1346 | 1347 | /* Free the result returned by dynstr_(rsplit|split)*(), 1348 | * or do nothing if 'tokens' is NULL. 1349 | **/ 1350 | void dynstr_freesplitres(dynstr* tokens, int count) 1351 | { 1352 | if (!tokens) return; 1353 | while (count--) 1354 | dynstr_free(tokens[count]); 1355 | free(tokens); 1356 | } 1357 | 1358 | static const char base64_standard[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1359 | "abcdefghijklmnopqrstuvwxyz0123456789+/"; 1360 | 1361 | static int decode_one_standard(char c) 1362 | { 1363 | if (c >= 'A' && c <= 'Z') 1364 | return (c - 'A'); 1365 | if (c >= 'a' && c <= 'z') 1366 | return (c - 'a' + 26); 1367 | if (c >= '0' && c <= '9') 1368 | return (c - '0' + 52); 1369 | if (c == '+') 1370 | return (62); 1371 | if (c == '/') 1372 | return (63); 1373 | if (c == '=') 1374 | return (-1); 1375 | 1376 | return (-2); 1377 | } 1378 | 1379 | dynstr b64encode_standard(const unsigned char* input, size_t len) 1380 | { 1381 | dynstr output; 1382 | char tmp[5]; 1383 | unsigned int i = 0; 1384 | 1385 | tmp[4] = '\0'; 1386 | 1387 | output = dynstr_newlen(NULL, (len + 2) / 3 * 4); 1388 | dynstr_clear(output); 1389 | while (i < len) { 1390 | uint32_t c = input[i++] << 16; 1391 | if (i < len) 1392 | c |= input[i] << 8; 1393 | i++; 1394 | if (i < len) 1395 | c |= input[i]; 1396 | i++; 1397 | 1398 | tmp[0] = base64_standard[(c & 0x00fc0000) >> 18]; 1399 | tmp[1] = base64_standard[(c & 0x0003f000) >> 12]; 1400 | tmp[2] = i > len + 1 ? '=' : base64_standard[(c & 0x00000fc0) >> 6]; 1401 | tmp[3] = i > len ? '=' : base64_standard[c & 0x0000003f]; 1402 | 1403 | output = dynstr_catlen(output, tmp, 4); 1404 | } 1405 | return output; 1406 | } 1407 | 1408 | dynstr b64decode_standard(const char* input, size_t len) 1409 | { 1410 | int typ[4]; 1411 | uint8_t buf[4]; 1412 | unsigned int i, j; 1413 | dynstr output; 1414 | 1415 | buf[3] = '\0'; 1416 | output = dynstr_newlen(NULL, len / 4 * 3); 1417 | dynstr_clear(output); 1418 | /* 1419 | * Valid encoded strings are a multiple of 4 characters long: 1420 | */ 1421 | if (len % 4 != 0) 1422 | return NULL; 1423 | 1424 | for (i = 0; i < len; i += 4) { 1425 | for (j = 0; j < 4; j++) 1426 | typ[j] = decode_one_standard(input[i + j]); 1427 | 1428 | /* 1429 | * Filler must be contiguous on the right of the input 1430 | * string, and at most two bytes: 1431 | */ 1432 | if (typ[0] == -1 || typ[1] == -1) 1433 | goto clean_up; 1434 | if (typ[2] == -1 && typ[3] != -1) 1435 | goto clean_up; 1436 | 1437 | buf[0] = (typ[0] << 2) | (typ[1] >> 4); 1438 | if (typ[2] != -1) 1439 | buf[1] = ((typ[1] & 0x0f) << 4) | (typ[2] >> 2); 1440 | else 1441 | buf[1] = '\0'; 1442 | if (typ[3] != -1) 1443 | buf[2] = ((typ[2] & 0x03) << 6) | typ[3]; 1444 | else 1445 | buf[2] = '\0'; 1446 | 1447 | output = dynstr_catlen(output, (const char*)buf, 3); 1448 | } 1449 | 1450 | return output; 1451 | clean_up: 1452 | dynstr_free(output); 1453 | return NULL; 1454 | } 1455 | 1456 | #ifdef DYNSTR_TEST_MAIN 1457 | #include 1458 | #include "testhelp.h" 1459 | 1460 | int main(void) 1461 | { 1462 | { 1463 | struct dynstr_hdr* sh; 1464 | dynstr x = dynstr_new("foo"), y; 1465 | 1466 | test_cond("Create a string and obtain the length", 1467 | dynstr_len(x) == 3 && memcmp(x, "foo\0", 4) == 0) 1468 | 1469 | dynstr_free(x); 1470 | x = dynstr_newlen("foo", 2); 1471 | test_cond("Create a string with specified length", 1472 | dynstr_len(x) == 2 && memcmp(x, "fo\0", 3) == 0) 1473 | 1474 | x = dynstr_cat(x, "bar"); 1475 | test_cond("Strings concatenation", 1476 | dynstr_len(x) == 5 && memcmp(x, "fobar\0", 6) == 0); 1477 | 1478 | x = dynstr_cpy(x, "a"); 1479 | test_cond("dynstr_cpy() against an originally longer string", 1480 | dynstr_len(x) == 1 && memcmp(x, "a\0", 2) == 0) 1481 | 1482 | x = dynstr_cpy(x, "xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); 1483 | test_cond("dynstr_cpy() against an originally shorter string", 1484 | dynstr_len(x) == 33 && 1485 | memcmp(x, "xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0", 33) == 0) 1486 | 1487 | dynstr_free(x); 1488 | x = dynstr_catprintf(dynstr_empty(), "%d", 123); 1489 | test_cond("dynstr_catprintf() seems working in the base case", 1490 | dynstr_len(x) == 3 && memcmp(x, "123\0", 4) == 0) 1491 | 1492 | dynstr_free(x); 1493 | x = dynstr_new("xxciaoyyy"); 1494 | dynstr_trim(x, "xy"); 1495 | test_cond("dynstr_trim() correctly trims characters", 1496 | dynstr_len(x) == 4 && memcmp(x, "ciao\0", 5) == 0) 1497 | 1498 | y = dynstr_dup(x); 1499 | dynstr_range(y, 1, 1); 1500 | test_cond("dynstr_range(...,1,1)", 1501 | dynstr_len(y) == 1 && memcmp(y, "i\0", 2) == 0) 1502 | 1503 | dynstr_free(y); 1504 | y = dynstr_dup(x); 1505 | dynstr_range(y, 1, -1); 1506 | test_cond("dynstr_range(...,1,-1)", 1507 | dynstr_len(y) == 3 && memcmp(y, "iao\0", 4) == 0) 1508 | 1509 | dynstr_free(y); 1510 | y = dynstr_dup(x); 1511 | dynstr_range(y, -2, -1); 1512 | test_cond("dynstr_range(...,-2,-1)", 1513 | dynstr_len(y) == 2 && memcmp(y, "ao\0", 3) == 0) 1514 | 1515 | dynstr_free(y); 1516 | y = dynstr_dup(x); 1517 | dynstr_range(y, 2, 1); 1518 | test_cond("dynstr_range(...,2,1)", 1519 | dynstr_len(y) == 0 && memcmp(y, "\0", 1) == 0) 1520 | 1521 | dynstr_free(y); 1522 | y = dynstr_dup(x); 1523 | dynstr_range(y, 1, 100); 1524 | test_cond("dynstr_range(...,1,100)", 1525 | dynstr_len(y) == 3 && memcmp(y, "iao\0", 4) == 0) 1526 | 1527 | dynstr_free(y); 1528 | y = dynstr_dup(x); 1529 | dynstr_range(y, 100, 100); 1530 | test_cond("dynstr_range(...,100,100)", 1531 | dynstr_len(y) == 0 && memcmp(y, "\0", 1) == 0) 1532 | 1533 | dynstr_free(y); 1534 | dynstr_free(x); 1535 | x = dynstr_new("foo"); 1536 | y = dynstr_new("foa"); 1537 | test_cond("dynstr_cmp(foo,foa)", dynstr_cmp(x, y) > 0) 1538 | 1539 | dynstr_free(y); 1540 | dynstr_free(x); 1541 | x = dynstr_new("bar"); 1542 | y = dynstr_new("bar"); 1543 | test_cond("dynstr_cmp(bar,bar)", dynstr_cmp(x, y) == 0) 1544 | 1545 | dynstr_free(y); 1546 | dynstr_free(x); 1547 | x = dynstr_new("aar"); 1548 | y = dynstr_new("bar"); 1549 | test_cond("dynstr_cmp(bar,bar)", dynstr_cmp(x, y) < 0) 1550 | 1551 | dynstr_free(y); 1552 | dynstr_free(x); 1553 | x = dynstr_newlen("\a\n\0foo\r", 7); 1554 | y = dynstr_catrepr(dynstr_empty(), x, dynstr_len(x)); 1555 | test_cond("dynstr_catrepr(...data...)", 1556 | memcmp(y, "\"\\a\\n\\x00foo\\r\"", 15) == 0) 1557 | 1558 | { 1559 | int oldfree; 1560 | 1561 | dynstr_free(x); 1562 | x = dynstr_new("0"); 1563 | sh = (void*)(x - (sizeof(struct dynstr_hdr))); 1564 | test_cond("dynstr_new() free/len buffers", sh->len == 1 && sh->free == 0); 1565 | x = dynstrMakeRoomFor(x, 1); 1566 | sh = (void*)(x - (sizeof(struct dynstr_hdr))); 1567 | test_cond("dynstrMakeRoomFor()", sh->len == 1 && sh->free > 0); 1568 | oldfree = sh->free; 1569 | x[1] = '1'; 1570 | dynstrIncrLen(x, 1); 1571 | test_cond("dynstrIncrLen() -- content", x[0] == '0' && x[1] == '1'); 1572 | test_cond("dynstrIncrLen() -- len", sh->len == 2); 1573 | test_cond("dynstrIncrLen() -- free", sh->free == oldfree - 1); 1574 | } 1575 | } 1576 | test_report() 1577 | return 0; 1578 | } 1579 | #endif --------------------------------------------------------------------------------