From 4172078d7b3a0d256b63b73d0fa8ba70c9a07e44 Mon Sep 17 00:00:00 2001 From: BlueMatthew Date: Tue, 19 Mar 2024 10:24:15 +0800 Subject: [PATCH] Initial Commit --- inc/cJSON.h | 293 +++ inc/common.h | 66 + inc/crc.h | 9 + inc/dynamiclibs.h | 8 + inc/iec61850_process.h | 38 + inc/main.h | 13 + inc/modbus-rtu.h | 42 + inc/modbus-tcp.h | 52 + inc/modbus-version.h | 53 + inc/modbus.h | 293 +++ inc/modbus_rtu_slave.h | 14 + inc/msq.h | 15 + inc/scl_shm.h | 137 ++ inc/serial.h | 17 + inc/sjzd.h | 55 + inc/sjzd_serial.h | 15 + inc/thread.h | 10 + src/EasyLogger/inc/elog.h | 273 +++ src/EasyLogger/inc/elog_cfg.h | 67 + src/EasyLogger/inc/elog_file.h | 72 + src/EasyLogger/inc/elog_file_cfg.h | 41 + src/EasyLogger/src/elog.c | 873 ++++++++ src/EasyLogger/src/elog_async.c | 352 +++ src/EasyLogger/src/elog_buf.c | 104 + src/EasyLogger/src/elog_file.c | 165 ++ src/EasyLogger/src/elog_file_port.c | 160 ++ src/EasyLogger/src/elog_port.c | 132 ++ src/EasyLogger/src/elog_utils.c | 103 + src/Makefile | 288 +++ src/cJSON.c | 3096 +++++++++++++++++++++++++++ src/common.c | 218 ++ src/crc.c | 93 + src/iec61850_process.c | 98 + src/main.c | 171 ++ src/modbus_rtu_slave.c | 263 +++ src/msq.c | 64 + src/serial.c | 229 ++ src/sjzd.c | 425 ++++ src/sjzd_serial.c | 91 + src/thread.c | 42 + 40 files changed, 8550 insertions(+) create mode 100644 inc/cJSON.h create mode 100644 inc/common.h create mode 100644 inc/crc.h create mode 100644 inc/dynamiclibs.h create mode 100644 inc/iec61850_process.h create mode 100644 inc/main.h create mode 100644 inc/modbus-rtu.h create mode 100644 inc/modbus-tcp.h create mode 100644 inc/modbus-version.h create mode 100644 inc/modbus.h create mode 100644 inc/modbus_rtu_slave.h create mode 100644 inc/msq.h create mode 100644 inc/scl_shm.h create mode 100644 inc/serial.h create mode 100644 inc/sjzd.h create mode 100644 inc/sjzd_serial.h create mode 100644 inc/thread.h create mode 100644 src/EasyLogger/inc/elog.h create mode 100644 src/EasyLogger/inc/elog_cfg.h create mode 100644 src/EasyLogger/inc/elog_file.h create mode 100644 src/EasyLogger/inc/elog_file_cfg.h create mode 100644 src/EasyLogger/src/elog.c create mode 100644 src/EasyLogger/src/elog_async.c create mode 100644 src/EasyLogger/src/elog_buf.c create mode 100644 src/EasyLogger/src/elog_file.c create mode 100644 src/EasyLogger/src/elog_file_port.c create mode 100644 src/EasyLogger/src/elog_port.c create mode 100644 src/EasyLogger/src/elog_utils.c create mode 100644 src/Makefile create mode 100644 src/cJSON.c create mode 100644 src/common.c create mode 100644 src/crc.c create mode 100644 src/iec61850_process.c create mode 100644 src/main.c create mode 100644 src/modbus_rtu_slave.c create mode 100644 src/msq.c create mode 100644 src/serial.c create mode 100644 src/sjzd.c create mode 100644 src/sjzd_serial.c create mode 100644 src/thread.c diff --git a/inc/cJSON.h b/inc/cJSON.h new file mode 100644 index 0000000..b5ceb29 --- /dev/null +++ b/inc/cJSON.h @@ -0,0 +1,293 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 13 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable adress area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/common.h b/inc/common.h new file mode 100644 index 0000000..d70ab3f --- /dev/null +++ b/inc/common.h @@ -0,0 +1,66 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +#define MAX_ITEM_LEN 32 + +#define CFG_FILE "dev_desc.json" + +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; +typedef int int32_t; +typedef short int16_t; + +typedef struct +{ + /* data */ + char port[MAX_ITEM_LEN]; + char addr[MAX_ITEM_LEN]; + char desc[MAX_ITEM_LEN]; + char model[MAX_ITEM_LEN]; +}DEV_DESC_TYPE; + +typedef struct +{ + /* data */ + char baud[MAX_ITEM_LEN]; + char bits[MAX_ITEM_LEN]; + char parity[MAX_ITEM_LEN]; +}PORT_CFG_TYPE; + +typedef struct +{ + /* data */ + DEV_DESC_TYPE dev_desc; + PORT_CFG_TYPE port_cfg; +}DEV_CFG_INFO; + +typedef struct +{ + /* data */ + uint32_t cmd; + uint32_t t; + uint32_t argv[250]; +}COM_MSG_TYPE; + + +extern DEV_CFG_INFO dev_cfg; + +char *get_time_in_json(void); +char *make_token(char *token,char len); +char *read_file(char *file_name,int32_t *ret); +int32_t read_dev_cfg(DEV_CFG_INFO *p); +cJSON *file_to_json(char *file); +int split(char *src,const char *separator,char **dest); + +int32_t save_to_file(char *file,uint32_t pos,uint8_t *buf,uint32_t size); +int32_t load_from_file(char *file,uint32_t pos,uint8_t *buf,uint32_t size); +#endif \ No newline at end of file diff --git a/inc/crc.h b/inc/crc.h new file mode 100644 index 0000000..d2d8e24 --- /dev/null +++ b/inc/crc.h @@ -0,0 +1,9 @@ +#ifndef _CRC_H +#define _CRC_H + +#include "common.h" + +uint16_t crc16(unsigned char *puchMsg,unsigned int usDataLen); +void crc16_ex(unsigned char *puchMsg,unsigned int usDataLen,unsigned char *result); + +#endif \ No newline at end of file diff --git a/inc/dynamiclibs.h b/inc/dynamiclibs.h new file mode 100644 index 0000000..40af700 --- /dev/null +++ b/inc/dynamiclibs.h @@ -0,0 +1,8 @@ +#ifndef __dynamic_libs_h__ +#define __dynamic_libs_h__ + +/* #include "apue.h" */ +int dynamic_lib_func_add(int i1, int i2); +int dynamic_lib_func_mul(int i1, int i2); + +#endif \ No newline at end of file diff --git a/inc/iec61850_process.h b/inc/iec61850_process.h new file mode 100644 index 0000000..99233e1 --- /dev/null +++ b/inc/iec61850_process.h @@ -0,0 +1,38 @@ +#ifndef _IEC61850_PROCESS_H +#define _IEC61850_PROCESS_H + +#include "scl_shm.h" + + +#define IEC61850_RX_MQ "/iec_rx_mq" +#define IEC61850_RX_MAX_MESSAGE 32 +#define IEC61850_RX_MESSAGE_SIZE 1024 +#define IEC61850_RX_MQ_PRIO 31 + + +#define CMD_SEND_PRI_DATA 16 + +typedef struct +{ + USR_MV AccPri[19]; + USR_MV AccSec[7]; + USR_MV EnvNoiPri; + USR_MV EnvNoiSec; + USR_MV NeuCur; + USR_MV MoDevConf; + USR_MV MoDevDetF; + USR_MV MoDevSigF; + USR_MV ModevPowF; + USR_MV EnvNoiPriAlm; + USR_MV EnvNoiSecAlm; + USR_MV NeuCurAlm; + USR_MV AccPriAlm[19]; + USR_MV AccSecAlm[7]; +}LN_SVBR_TYPE; + + +extern int iec61850_rx_init(void); +extern int iec61850_send_msg(void *msg,int msg_len); + + +#endif \ No newline at end of file diff --git a/inc/main.h b/inc/main.h new file mode 100644 index 0000000..1f2ebc2 --- /dev/null +++ b/inc/main.h @@ -0,0 +1,13 @@ +#ifndef _MAIN_H +#define _MAIN_H + +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +#endif diff --git a/inc/modbus-rtu.h b/inc/modbus-rtu.h new file mode 100644 index 0000000..214a888 --- /dev/null +++ b/inc/modbus-rtu.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2001-2011 Stéphane Raimbault + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef MODBUS_RTU_H +#define MODBUS_RTU_H + +#include "modbus.h" + +MODBUS_BEGIN_DECLS + +/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 + * RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes + */ +#define MODBUS_RTU_MAX_ADU_LENGTH 256 + +MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity, + int data_bit, int stop_bit); + +#define MODBUS_RTU_RS232 0 +#define MODBUS_RTU_RS485 1 + +MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode); +MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx); + +#define MODBUS_RTU_RTS_NONE 0 +#define MODBUS_RTU_RTS_UP 1 +#define MODBUS_RTU_RTS_DOWN 2 + +MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode); +MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx); + +MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on)); + +MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us); +MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx); + +MODBUS_END_DECLS + +#endif /* MODBUS_RTU_H */ diff --git a/inc/modbus-tcp.h b/inc/modbus-tcp.h new file mode 100644 index 0000000..abaef27 --- /dev/null +++ b/inc/modbus-tcp.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2001-2010 Stéphane Raimbault + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef MODBUS_TCP_H +#define MODBUS_TCP_H + +#include "modbus.h" + +MODBUS_BEGIN_DECLS + +#if defined(_WIN32) && !defined(__CYGWIN__) +/* Win32 with MinGW, supplement to */ +#include +#if !defined(ECONNRESET) +#define ECONNRESET WSAECONNRESET +#endif +#if !defined(ECONNREFUSED) +#define ECONNREFUSED WSAECONNREFUSED +#endif +#if !defined(ETIMEDOUT) +#define ETIMEDOUT WSAETIMEDOUT +#endif +#if !defined(ENOPROTOOPT) +#define ENOPROTOOPT WSAENOPROTOOPT +#endif +#if !defined(EINPROGRESS) +#define EINPROGRESS WSAEINPROGRESS +#endif +#endif + +#define MODBUS_TCP_DEFAULT_PORT 502 +#define MODBUS_TCP_SLAVE 0xFF + +/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5 + * TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes + */ +#define MODBUS_TCP_MAX_ADU_LENGTH 260 + +MODBUS_API modbus_t* modbus_new_tcp(const char *ip_address, int port); +MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection); +MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s); + +MODBUS_API modbus_t* modbus_new_tcp_pi(const char *node, const char *service); +MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection); +MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s); + +MODBUS_END_DECLS + +#endif /* MODBUS_TCP_H */ diff --git a/inc/modbus-version.h b/inc/modbus-version.h new file mode 100644 index 0000000..0218973 --- /dev/null +++ b/inc/modbus-version.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2010-2014 Stéphane Raimbault + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MODBUS_VERSION_H +#define MODBUS_VERSION_H + +/* The major version, (1, if %LIBMODBUS_VERSION is 1.2.3) */ +#define LIBMODBUS_VERSION_MAJOR (3) + +/* The minor version (2, if %LIBMODBUS_VERSION is 1.2.3) */ +#define LIBMODBUS_VERSION_MINOR (1) + +/* The micro version (3, if %LIBMODBUS_VERSION is 1.2.3) */ +#define LIBMODBUS_VERSION_MICRO (6) + +/* The full version, like 1.2.3 */ +#define LIBMODBUS_VERSION 3.1.6 + +/* The full version, in string form (suited for string concatenation) + */ +#define LIBMODBUS_VERSION_STRING "3.1.6" + +/* Numerically encoded version, eg. v1.2.3 is 0x010203 */ +#define LIBMODBUS_VERSION_HEX ((LIBMODBUS_VERSION_MAJOR << 16) | \ + (LIBMODBUS_VERSION_MINOR << 8) | \ + (LIBMODBUS_VERSION_MICRO << 0)) + +/* Evaluates to True if the version is greater than @major, @minor and @micro + */ +#define LIBMODBUS_VERSION_CHECK(major,minor,micro) \ + (LIBMODBUS_VERSION_MAJOR > (major) || \ + (LIBMODBUS_VERSION_MAJOR == (major) && \ + LIBMODBUS_VERSION_MINOR > (minor)) || \ + (LIBMODBUS_VERSION_MAJOR == (major) && \ + LIBMODBUS_VERSION_MINOR == (minor) && \ + LIBMODBUS_VERSION_MICRO >= (micro))) + +#endif /* MODBUS_VERSION_H */ diff --git a/inc/modbus.h b/inc/modbus.h new file mode 100644 index 0000000..fbe20bc --- /dev/null +++ b/inc/modbus.h @@ -0,0 +1,293 @@ +/* + * Copyright © 2001-2013 Stéphane Raimbault + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef MODBUS_H +#define MODBUS_H + +/* Add this for macros that defined unix flavor */ +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include +#endif + +#ifndef _MSC_VER +#include +#else +#include "stdint.h" +#endif + +#include "modbus-version.h" + +#if defined(_MSC_VER) +# if defined(DLLBUILD) +/* define DLLBUILD when building the DLL */ +# define MODBUS_API __declspec(dllexport) +# else +# define MODBUS_API __declspec(dllimport) +# endif +#else +# define MODBUS_API +#endif + +#ifdef __cplusplus +# define MODBUS_BEGIN_DECLS extern "C" { +# define MODBUS_END_DECLS } +#else +# define MODBUS_BEGIN_DECLS +# define MODBUS_END_DECLS +#endif + +MODBUS_BEGIN_DECLS + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef OFF +#define OFF 0 +#endif + +#ifndef ON +#define ON 1 +#endif + +/* Modbus function codes */ +#define MODBUS_FC_READ_COILS 0x01 +#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02 +#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03 +#define MODBUS_FC_READ_INPUT_REGISTERS 0x04 +#define MODBUS_FC_WRITE_SINGLE_COIL 0x05 +#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06 +#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07 +#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F +#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 +#define MODBUS_FC_REPORT_SLAVE_ID 0x11 +#define MODBUS_FC_MASK_WRITE_REGISTER 0x16 +#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17 + +#define MODBUS_BROADCAST_ADDRESS 0 + +/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12) + * Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0) + * (chapter 6 section 11 page 29) + * Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0) + */ +#define MODBUS_MAX_READ_BITS 2000 +#define MODBUS_MAX_WRITE_BITS 1968 + +/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15) + * Quantity of Registers to read (2 bytes): 1 to 125 (0x7D) + * (chapter 6 section 12 page 31) + * Quantity of Registers to write (2 bytes) 1 to 123 (0x7B) + * (chapter 6 section 17 page 38) + * Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79) + */ +#define MODBUS_MAX_READ_REGISTERS 125 +#define MODBUS_MAX_WRITE_REGISTERS 123 +#define MODBUS_MAX_WR_WRITE_REGISTERS 121 +#define MODBUS_MAX_WR_READ_REGISTERS 125 + +/* The size of the MODBUS PDU is limited by the size constraint inherited from + * the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256 + * bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server + * address (1 byte) - CRC (2 bytes) = 253 bytes. + */ +#define MODBUS_MAX_PDU_LENGTH 253 + +/* Consequently: + * - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256 + * bytes. + * - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes. + * so the maximum of both backend in 260 bytes. This size can used to allocate + * an array of bytes to store responses and it will be compatible with the two + * backends. + */ +#define MODBUS_MAX_ADU_LENGTH 260 + +/* Random number to avoid errno conflicts */ +#define MODBUS_ENOBASE 112345678 + +/* Protocol exceptions */ +enum { + MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01, + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE, + MODBUS_EXCEPTION_ACKNOWLEDGE, + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY, + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE, + MODBUS_EXCEPTION_MEMORY_PARITY, + MODBUS_EXCEPTION_NOT_DEFINED, + MODBUS_EXCEPTION_GATEWAY_PATH, + MODBUS_EXCEPTION_GATEWAY_TARGET, + MODBUS_EXCEPTION_MAX +}; + +#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION) +#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS) +#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE) +#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE) +#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE) +#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY) +#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE) +#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY) +#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH) +#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET) + +/* Native libmodbus error codes */ +#define EMBBADCRC (EMBXGTAR + 1) +#define EMBBADDATA (EMBXGTAR + 2) +#define EMBBADEXC (EMBXGTAR + 3) +#define EMBUNKEXC (EMBXGTAR + 4) +#define EMBMDATA (EMBXGTAR + 5) +#define EMBBADSLAVE (EMBXGTAR + 6) + +extern const unsigned int libmodbus_version_major; +extern const unsigned int libmodbus_version_minor; +extern const unsigned int libmodbus_version_micro; + +typedef struct _modbus modbus_t; + +typedef struct _modbus_mapping_t { + int nb_bits; + int start_bits; + int nb_input_bits; + int start_input_bits; + int nb_input_registers; + int start_input_registers; + int nb_registers; + int start_registers; + uint8_t *tab_bits; + uint8_t *tab_input_bits; + uint16_t *tab_input_registers; + uint16_t *tab_registers; +} modbus_mapping_t; + +typedef enum +{ + MODBUS_ERROR_RECOVERY_NONE = 0, + MODBUS_ERROR_RECOVERY_LINK = (1<<1), + MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2) +} modbus_error_recovery_mode; + +MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave); +MODBUS_API int modbus_get_slave(modbus_t* ctx); +MODBUS_API int modbus_set_error_recovery(modbus_t *ctx, modbus_error_recovery_mode error_recovery); +MODBUS_API int modbus_set_socket(modbus_t *ctx, int s); +MODBUS_API int modbus_get_socket(modbus_t *ctx); + +MODBUS_API int modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +MODBUS_API int modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); + +MODBUS_API int modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); + +MODBUS_API int modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); +MODBUS_API int modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); + +MODBUS_API int modbus_get_header_length(modbus_t *ctx); + +MODBUS_API int modbus_connect(modbus_t *ctx); +MODBUS_API void modbus_close(modbus_t *ctx); + +MODBUS_API void modbus_free(modbus_t *ctx); + +MODBUS_API int modbus_flush(modbus_t *ctx); +MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag); + +MODBUS_API const char *modbus_strerror(int errnum); + +MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); +MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); +MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); +MODBUS_API int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); +MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status); +MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value); +MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data); +MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); +MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask); +MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb, + const uint16_t *src, int read_addr, int read_nb, + uint16_t *dest); +MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest); + +MODBUS_API modbus_mapping_t* modbus_mapping_new_start_address( + unsigned int start_bits, unsigned int nb_bits, + unsigned int start_input_bits, unsigned int nb_input_bits, + unsigned int start_registers, unsigned int nb_registers, + unsigned int start_input_registers, unsigned int nb_input_registers); + +MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, + int nb_registers, int nb_input_registers); +MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping); + +MODBUS_API int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length); + +MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req); + +MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp); + +MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req, + int req_length, modbus_mapping_t *mb_mapping); +MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req, + unsigned int exception_code); + +/** + * UTILS FUNCTIONS + **/ + +#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF) +#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF) +#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \ + (((int64_t)tab_int16[(index) ] << 48) + \ + ((int64_t)tab_int16[(index) + 1] << 32) + \ + ((int64_t)tab_int16[(index) + 2] << 16) + \ + (int64_t)tab_int16[(index) + 3]) +#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) ((tab_int16[(index)] << 16) + tab_int16[(index) + 1]) +#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) ((tab_int8[(index)] << 8) + tab_int8[(index) + 1]) +#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \ + do { \ + tab_int8[(index)] = (value) >> 8; \ + tab_int8[(index) + 1] = (value) & 0xFF; \ + } while (0) +#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \ + do { \ + tab_int16[(index) ] = (value) >> 16; \ + tab_int16[(index) + 1] = (value); \ + } while (0) +#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \ + do { \ + tab_int16[(index) ] = (value) >> 48; \ + tab_int16[(index) + 1] = (value) >> 32; \ + tab_int16[(index) + 2] = (value) >> 16; \ + tab_int16[(index) + 3] = (value); \ + } while (0) + +MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value); +MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest, int idx, unsigned int nb_bits, + const uint8_t *tab_byte); +MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src, int idx, unsigned int nb_bits); +MODBUS_API float modbus_get_float(const uint16_t *src); +MODBUS_API float modbus_get_float_abcd(const uint16_t *src); +MODBUS_API float modbus_get_float_dcba(const uint16_t *src); +MODBUS_API float modbus_get_float_badc(const uint16_t *src); +MODBUS_API float modbus_get_float_cdab(const uint16_t *src); + +MODBUS_API void modbus_set_float(float f, uint16_t *dest); +MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest); +MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest); +MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest); +MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest); + +#include "modbus-tcp.h" +#include "modbus-rtu.h" + +MODBUS_END_DECLS + +#endif /* MODBUS_H */ diff --git a/inc/modbus_rtu_slave.h b/inc/modbus_rtu_slave.h new file mode 100644 index 0000000..8025bce --- /dev/null +++ b/inc/modbus_rtu_slave.h @@ -0,0 +1,14 @@ +#ifndef _MODBUS_RTU_SLAVE_H +#define _MODBUS_RTU_SLAVE_H + +//J24.1 J24.2 ===> A-B +#define MODBUS_RTU_SERIAL "/dev/ttyO3" +#define MODBUS_RTU_BAUD 38400 +#define SERVER_ID 1 + + +//extern YSP_MODBUS_REGS ysp_modbus_data; + +int modbus_rtu_slave_init(void); +void modbus_rtu_data_fresh(void *data,int is_new); +#endif \ No newline at end of file diff --git a/inc/msq.h b/inc/msq.h new file mode 100644 index 0000000..7911fe3 --- /dev/null +++ b/inc/msq.h @@ -0,0 +1,15 @@ +#ifndef _MSQ_H +#define _MSQ_H + +#include +#include +#include +#include +#include +#include + +mqd_t create_mq(const char *mq_name,long int mq_maxmsg,long int mq_msgsize); +mqd_t open_mq(const char *mq_name); +ssize_t recv_mq_wait(mqd_t mq,void *msg,int size,int timeouts); + +#endif \ No newline at end of file diff --git a/inc/scl_shm.h b/inc/scl_shm.h new file mode 100644 index 0000000..88234bb --- /dev/null +++ b/inc/scl_shm.h @@ -0,0 +1,137 @@ +#ifndef _SHM_H +#define _SHM_H + + +#define TYPE_MX 1 +#define TYPE_ST 2 +#define TYPE_SE 3 +#define TYPE_SG 4 +#define TYPE_SGCB 5 +#define TYPE_CO 6 +#define TYPE_SP 7 +#define TYPE_SV 8 + +#define CMD_WR_MX 1 +#define CMD_WR_ST 2 +#define CMD_WR_SE 3 +#define CMD_WR_SG 4 +#define CMD_WR_SGCB 5 +#define CMD_WR_CO 6 +#define CMD_WR_SP 7 +#define CMD_WR_SV 8 + +typedef union +{ +float f; +int i; +char stVal; +void *ptr; +}GEN_VAL; + +typedef struct{ +unsigned int secs; +unsigned int ms; +}TimeStamp; + + +typedef struct utc_time_tag +{ + unsigned int secs; // Number of seconds since January 1, 1970 + unsigned int fraction; // Fraction of a second + unsigned int qflags; // Quality flags, 8 least-significant bits only +} UTC_TIME; + + +typedef struct{ +GEN_VAL v; +int q; +TimeStamp t; +//#ifdef SUB_ENABLE +int subEna; +GEN_VAL subVal; +int subQ; +//#endif +}SCL_MV; // + + +typedef struct{ +GEN_VAL v; +int q; +TimeStamp t; +}USR_MV; // + + +typedef struct{ +GEN_VAL ctlVal; +char ctlNum; +TimeStamp t; +char Test; +char Check; +int valType; +}SCL_CO; + +typedef struct +{ +unsigned char NumOfSG; //1-n +unsigned char ActSG; //1-n +unsigned char EditSG; //0-n,0 +unsigned char CnfEdit; +UTC_TIME LActTm;// TimeStamp +}SGCB_CTRL; + + +typedef union +{ +USR_MV ana_mv; +SCL_MV scl_mv; +SCL_CO scl_co; +SGCB_CTRL sgcb_ctrl; +}SCL_VAL; + + +typedef struct{ +int type; +int grp; +int num; +int ofs; +GEN_VAL v; +}SCL_DATA_SPEC; + +typedef struct{ +int cmd; +union +{ + SCL_DATA_SPEC data_spec; +}u; +}SCL_MSG_TYPE; + + +int start_scl_mem(); +int stop_scl_mem(); +void lock_scl_mem(); +void unlock_scl_mem(); + + +char *put_scl_data(int grp,int index,void *ptr,int num_bytes); +char *get_scl_data(int grp,int index,void *ptr,int num_bytes); + +SCL_VAL* put_mv_data(int grp,int index,USR_MV *usr_mv,int num_do); +SCL_VAL* get_mv_data(int grp,int index,USR_MV *usr_data,int num_do); +SCL_VAL *put_do_data(int grp,int index,SCL_VAL *scl_do,int num_of_do); +SCL_VAL *get_do_data(int grp,int index,SCL_VAL *scl_do,int num_of_do); + +SGCB_CTRL* get_sgcb_data(int index,SGCB_CTRL *sgcb); +SGCB_CTRL* put_sgcb_data(int index,SGCB_CTRL *sgcb); +char *get_sg_data(int grp,int index,char *ptr,int num_bytes); +char *put_sg_data(int grp,int index,char *ptr,int num_bytes); +char *get_se_data(int grp,int index,char *ptr,int num_bytes); +char *put_se_data(int grp,int index,char *ptr,int num_bytes); + +int get_usr_data(int offs,void *ptr,int num_bytes); +int put_usr_data(int offs,void *ptr,int num_bytes); + +int write_scl_msg(int cmd,SCL_DATA_SPEC *data_spec); + + +#endif + diff --git a/inc/serial.h b/inc/serial.h new file mode 100644 index 0000000..9a84d6f --- /dev/null +++ b/inc/serial.h @@ -0,0 +1,17 @@ +#ifndef _SERIAL_H +#define _SERIAL_H + +#include +#include +#include +#include +#include +#include +#include + +int SerialOpen(const char *dev_name,int baud,int databit,int stopbit,int chk_mode); +int SerialRead(int m_comFd,unsigned char *buf,int sz,int timeout); +int SerialWrite(int m_comFd,const unsigned char *buf,int sz); +int SerialReadEx(int m_comFd,unsigned char *buf,int sz,int timeout); + +#endif diff --git a/inc/sjzd.h b/inc/sjzd.h new file mode 100644 index 0000000..5fa40ef --- /dev/null +++ b/inc/sjzd.h @@ -0,0 +1,55 @@ +#ifndef _MODBUS_H +#define _MODBUS_H + +#include "crc.h" +#include "common.h" +#include "string.h" +#include "sjzd_serial.h" + +#define FRM_ERR_NONE 0 +#define FRM_ERR_START -1 +#define FRM_ERR_RX_TIMEOUT -2 +#define FRM_ERR_SLAVE_ADDR -3 +#define FRM_ERR_FC -4 +#define FRM_ERR_BYTE_COUNT -5 +#define FRM_ERR_CRC -6 + + +#define MAX_PAYLOAD_SIZE 1024 +#define FRM_MAX_LEN (MAX_PAYLOAD_SIZE+8) +#define FRM_HEADER_LEN 4 + +#define SJZD_RD_REGS 1 +#define SJZD_WR_REGS 2 +#define SJZD_RD_FILE_START 3 +#define SJZD_RD_FILE_CONTENT 4 +#define SJZD_RD_FILE_END 5 + +typedef struct +{ + float AccPri[19]; + float AccSec[7]; + float EnvNoiPri; + float EnvNoiSec; + float NeuCur; + int MoDevConf; + int MoDevDetF; + int MoDevSigF; + int ModevPowF; + int EnvNoiPriAlm; + int EnvNoiSecAlm; + int NeuCurAlm; + int AccPriAlm[19]; + int AccSecAlm[7]; +}SVBR_PRI_TYPE; + + +int32_t sjzd_master_init(char *port,char *baud,char *parity); +int32_t sjzd_make_frm(uint8_t *enc_buf,uint8_t slave_addr,uint8_t cmd,uint8_t *data,uint16_t data_len); +int32_t sjzd_chk_frm(uint8_t *buf,uint16_t len,uint8_t slave_addr,uint8_t cmd); + +int32_t sjzd_reg_rd(uint8_t slave_node,uint32_t reg_addr,uint8_t *reg_tbl,uint32_t nbr_reg); +int32_t sjzd_reg_wr(uint8_t slave_node,uint32_t reg_addr,uint8_t *reg_tbl,uint32_t nbr_reg,uint8_t *rlt); +int32_t sjzd_file_rd(uint8_t slave_node,char *in_file,char *out_file); + +#endif \ No newline at end of file diff --git a/inc/sjzd_serial.h b/inc/sjzd_serial.h new file mode 100644 index 0000000..4978793 --- /dev/null +++ b/inc/sjzd_serial.h @@ -0,0 +1,15 @@ +#ifndef _MODBUS_SERIAL_H +#define _MODBUS_SERIAL_H + +#include "serial.h" +#include "common.h" +#include + +#define MODBUS_TIMEOUT 10 + +int32_t sjzd_serial_init(char *port,int baud,char databit,char parity); +int32_t sjzd_serial_read(uint8_t *pucBuffer,uint16_t usNBytes,uint16_t *usNBytesRead); +int32_t sjzd_serial_write(uint8_t *pucBuffer,uint16_t usNBytes); + +#endif + diff --git a/inc/thread.h b/inc/thread.h new file mode 100644 index 0000000..9343774 --- /dev/null +++ b/inc/thread.h @@ -0,0 +1,10 @@ +#ifndef _THREAD_H +#define _THREAD_H + +#include +#include + +extern pthread_t task_create(void *(*start_routine)(void *),void *arg,int prio,int stacksize); + +#endif + diff --git a/src/EasyLogger/inc/elog.h b/src/EasyLogger/inc/elog.h new file mode 100644 index 0000000..0e5034c --- /dev/null +++ b/src/EasyLogger/inc/elog.h @@ -0,0 +1,273 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2019, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for this library. You can see all be called functions. + * Created on: 2015-04-28 + */ + +#ifndef __ELOG_H__ +#define __ELOG_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* output log's level */ +#define ELOG_LVL_ASSERT 0 +#define ELOG_LVL_ERROR 1 +#define ELOG_LVL_WARN 2 +#define ELOG_LVL_INFO 3 +#define ELOG_LVL_DEBUG 4 +#define ELOG_LVL_VERBOSE 5 + +/* the output silent level and all level for filter setting */ +#define ELOG_FILTER_LVL_SILENT ELOG_LVL_ASSERT +#define ELOG_FILTER_LVL_ALL ELOG_LVL_VERBOSE + +/* output log's level total number */ +#define ELOG_LVL_TOTAL_NUM 6 + +/* EasyLogger software version number */ +#define ELOG_SW_VERSION "2.2.99" + +/* EasyLogger assert for developer. */ +#ifdef ELOG_ASSERT_ENABLE + #define ELOG_ASSERT(EXPR) \ + if (!(EXPR)) \ + { \ + if (elog_assert_hook == NULL) { \ + elog_a("elog", "(%s) has assert failed at %s:%ld.", #EXPR, __FUNCTION__, __LINE__); \ + while (1); \ + } else { \ + elog_assert_hook(#EXPR, __FUNCTION__, __LINE__); \ + } \ + } +#else + #define ELOG_ASSERT(EXPR) ((void)0); +#endif + +#ifndef ELOG_OUTPUT_ENABLE + #define elog_assert(tag, ...) + #define elog_error(tag, ...) + #define elog_warn(tag, ...) + #define elog_info(tag, ...) + #define elog_debug(tag, ...) + #define elog_verbose(tag, ...) +#else /* ELOG_OUTPUT_ENABLE */ + #if ELOG_OUTPUT_LVL >= ELOG_LVL_ASSERT + #define elog_assert(tag, ...) \ + elog_output(ELOG_LVL_ASSERT, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_assert(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_ASSERT */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_ERROR + #define elog_error(tag, ...) \ + elog_output(ELOG_LVL_ERROR, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_error(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_ERROR */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_WARN + #define elog_warn(tag, ...) \ + elog_output(ELOG_LVL_WARN, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_warn(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_WARN */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_INFO + #define elog_info(tag, ...) \ + elog_output(ELOG_LVL_INFO, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_info(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_INFO */ + + #if ELOG_OUTPUT_LVL >= ELOG_LVL_DEBUG + #define elog_debug(tag, ...) \ + elog_output(ELOG_LVL_DEBUG, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_debug(tag, ...) + #endif /* ELOG_OUTPUT_LVL >= ELOG_LVL_DEBUG */ + + #if ELOG_OUTPUT_LVL == ELOG_LVL_VERBOSE + #define elog_verbose(tag, ...) \ + elog_output(ELOG_LVL_VERBOSE, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) + #else + #define elog_verbose(tag, ...) + #endif /* ELOG_OUTPUT_LVL == ELOG_LVL_VERBOSE */ +#endif /* ELOG_OUTPUT_ENABLE */ + +/* all formats index */ +typedef enum { + ELOG_FMT_LVL = 1 << 0, /**< level 级别*/ + ELOG_FMT_TAG = 1 << 1, /**< tag 标签*/ + ELOG_FMT_TIME = 1 << 2, /**< current time 当前时间*/ + ELOG_FMT_P_INFO = 1 << 3, /**< process info 进程信息*/ + ELOG_FMT_T_INFO = 1 << 4, /**< thread info 线程信息*/ + ELOG_FMT_DIR = 1 << 5, /**< file directory and name 文件路径和文件名*/ + ELOG_FMT_FUNC = 1 << 6, /**< function name 函数名称*/ + ELOG_FMT_LINE = 1 << 7, /**< line number 行号*/ +} ElogFmtIndex; + +/* macro definition for all formats */ +#define ELOG_FMT_ALL (ELOG_FMT_LVL|ELOG_FMT_TAG|ELOG_FMT_TIME|ELOG_FMT_P_INFO|ELOG_FMT_T_INFO| \ + ELOG_FMT_DIR|ELOG_FMT_FUNC|ELOG_FMT_LINE) + +/* output log's tag filter */ +typedef struct { + uint8_t level; + char tag[ELOG_FILTER_TAG_MAX_LEN + 1]; + bool tag_use_flag; /**< false : tag is no used true: tag is used */ +} ElogTagLvlFilter, *ElogTagLvlFilter_t; + +/* output log's filter */ +typedef struct { + uint8_t level; + char tag[ELOG_FILTER_TAG_MAX_LEN + 1]; + char keyword[ELOG_FILTER_KW_MAX_LEN + 1]; + ElogTagLvlFilter tag_lvl[ELOG_FILTER_TAG_LVL_MAX_NUM]; +} ElogFilter, *ElogFilter_t; + +/* easy logger */ +typedef struct { + ElogFilter filter; + size_t enabled_fmt_set[ELOG_LVL_TOTAL_NUM]; + bool init_ok; + bool output_enabled; + bool output_lock_enabled; + bool output_is_locked_before_enable; + bool output_is_locked_before_disable; + +#ifdef ELOG_COLOR_ENABLE + bool text_color_enabled; +#endif + +}EasyLogger, *EasyLogger_t; + +/* EasyLogger error code */ +typedef enum { + ELOG_NO_ERR, +} ElogErrCode; + +/* elog.c */ +ElogErrCode elog_init(void); +void elog_start(void); +void elog_set_output_enabled(bool enabled); +bool elog_get_output_enabled(void); +void elog_set_text_color_enabled(bool enabled); +bool elog_get_text_color_enabled(void); +void elog_set_fmt(uint8_t level, size_t set);//每个输出级别都可以单独设置输出内容格式 +void elog_set_filter(uint8_t level, const char *tag, const char *keyword); +void elog_set_filter_lvl(uint8_t level);//设置输出过滤级别 +void elog_set_filter_tag(const char *tag);//设置输出过滤标签 +void elog_set_filter_kw(const char *keyword); +void elog_set_filter_tag_lvl(const char *tag, uint8_t level); +uint8_t elog_get_filter_tag_lvl(const char *tag); +void elog_raw(const char *format, ...); +void elog_output(uint8_t level, const char *tag, const char *file, const char *func, + const long line, const char *format, ...); +void elog_output_lock_enabled(bool enabled); +extern void (*elog_assert_hook)(const char* expr, const char* func, size_t line); +void elog_assert_set_hook(void (*hook)(const char* expr, const char* func, size_t line)); +int8_t elog_find_lvl(const char *log); +const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len); +void elog_hexdump(const char *name, uint8_t width, uint8_t *buf, uint16_t size); + +#define elog_a(tag, ...) elog_assert(tag, __VA_ARGS__) +#define elog_e(tag, ...) elog_error(tag, __VA_ARGS__) +#define elog_w(tag, ...) elog_warn(tag, __VA_ARGS__) +#define elog_i(tag, ...) elog_info(tag, __VA_ARGS__) +#define elog_d(tag, ...) elog_debug(tag, __VA_ARGS__) +#define elog_v(tag, ...) elog_verbose(tag, __VA_ARGS__) + +/** + * log API short definition + * NOTE: The `LOG_TAG` and `LOG_LVL` must defined before including the when you want to use log_x API. + */ +#if !defined(LOG_TAG) + #define LOG_TAG "NO_TAG" +#endif +#if !defined(LOG_LVL) + #define LOG_LVL ELOG_LVL_VERBOSE +#endif +#if LOG_LVL >= ELOG_LVL_ASSERT + #define log_a(...) elog_a(LOG_TAG, __VA_ARGS__) +#else + #define log_a(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_ERROR + #define log_e(...) elog_e(LOG_TAG, __VA_ARGS__) +#else + #define log_e(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_WARN + #define log_w(...) elog_w(LOG_TAG, __VA_ARGS__) +#else + #define log_w(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_INFO + #define log_i(...) elog_i(LOG_TAG, __VA_ARGS__) +#else + #define log_i(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_DEBUG + #define log_d(...) elog_d(LOG_TAG, __VA_ARGS__) +#else + #define log_d(...) ((void)0); +#endif +#if LOG_LVL >= ELOG_LVL_VERBOSE + #define log_v(...) elog_v(LOG_TAG, __VA_ARGS__) +#else + #define log_v(...) ((void)0); +#endif + +/* assert API short definition */ +#if !defined(assert) + #define assert ELOG_ASSERT +#endif + +/* elog_buf.c */ +void elog_buf_enabled(bool enabled); +void elog_flush(void); + +/* elog_async.c */ +void elog_async_enabled(bool enabled); +size_t elog_async_get_log(char *log, size_t size); +size_t elog_async_get_line_log(char *log, size_t size); + +/* elog_utils.c */ +size_t elog_strcpy(size_t cur_len, char *dst, const char *src); +size_t elog_cpyln(char *line, const char *log, size_t len); +void *elog_memcpy(void *dst, const void *src, size_t count); + +#ifdef __cplusplus +} +#endif + +#endif /* __ELOG_H__ */ diff --git a/src/EasyLogger/inc/elog_cfg.h b/src/EasyLogger/inc/elog_cfg.h new file mode 100644 index 0000000..0ca7a95 --- /dev/null +++ b/src/EasyLogger/inc/elog_cfg.h @@ -0,0 +1,67 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this library. + * Created on: 2015-07-30 + */ + +#ifndef _ELOG_CFG_H_ +#define _ELOG_CFG_H_ + +/* enable log output. default open this macro 日志输出使能*/ +#define ELOG_OUTPUT_ENABLE +/* enable log write file. default open this macro 日志存入文件使能*/ +#define ELOG_FILE_ENABLE +/* enable flush file cache. default open this macro 使能日志文件缓冲*/ +#define ELOG_FILE_FLUSH_CAHCE_ENABLE +/* setting static output log level 设置日志静态输出级别,级别高于它的都会输出(值大于它),低于它的不输出*/ +#define ELOG_OUTPUT_LVL ELOG_LVL_VERBOSE +/* enable assert check 使能assert*/ +#define ELOG_ASSERT_ENABLE +/* buffer size for every line's log 日志行缓冲大小*/ +#define ELOG_LINE_BUF_SIZE 512 +/* output line number max length */ +#define ELOG_LINE_NUM_MAX_LEN 5 +/* output filter's tag max length 标签的最大长度,就是日志行首部的字符串*/ +#define ELOG_FILTER_TAG_MAX_LEN 16 +/* output filter's keyword max length */ +#define ELOG_FILTER_KW_MAX_LEN 16 +/* output filter's tag level max num */ +#define ELOG_FILTER_TAG_LVL_MAX_NUM 5 +/* output newline sign */ +#define ELOG_NEWLINE_SIGN "\n" +/* enable log color */ +#define ELOG_COLOR_ENABLE +/* enable asynchronous output mode 使能异步输出模式*/ +#define ELOG_ASYNC_OUTPUT_ENABLE +/* the highest output level for async mode, other level will sync output 异步输出的最高级别*/ +#define ELOG_ASYNC_OUTPUT_LVL ELOG_LVL_DEBUG +/* buffer size for asynchronous output mode 异步输出模式的缓冲区大小*/ +#define ELOG_ASYNC_OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE*100) +/* each asynchronous output's log which must end with newline sign */ +//#define ELOG_ASYNC_LINE_OUTPUT +/* asynchronous output mode using POSIX pthread implementation */ +#define ELOG_ASYNC_OUTPUT_USING_PTHREAD + +#endif /* _ELOG_CFG_H_ */ diff --git a/src/EasyLogger/inc/elog_file.h b/src/EasyLogger/inc/elog_file.h new file mode 100644 index 0000000..8b03f28 --- /dev/null +++ b/src/EasyLogger/inc/elog_file.h @@ -0,0 +1,72 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2019, Qintl, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is an head file for file log plugin. You can see all be called functions. + * Created on: 2019-01-05 + */ + +#ifndef __ELOG_FILE__H__ +#define __ELOG_FILE__H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* EasyLogger file log plugin's software version number */ +#define ELOG_FILE_SW_VERSION "V1.0.0" +#ifdef linux +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +typedef struct { + char *name; /* file name */ + size_t max_size; /* file max size */ + int max_rotate; /* max rotate file count */ +} ElogFileCfg; + +/* elog_file.c */ +ElogErrCode elog_file_init(void); +void elog_file_write(const char *log, size_t size); +void elog_file_config(ElogFileCfg *cfg); +void elog_file_deinit(void); + +/* elog_file_port.c */ +ElogErrCode elog_file_port_init(void); +void elog_file_port_lock(void); +void elog_file_port_unlock(void); +void elog_file_port_deinit(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/EasyLogger/inc/elog_file_cfg.h b/src/EasyLogger/inc/elog_file_cfg.h new file mode 100644 index 0000000..2105c5c --- /dev/null +++ b/src/EasyLogger/inc/elog_file_cfg.h @@ -0,0 +1,41 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2019, Qintl, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: It is the configure head file for this flash log plugin. + * Created on: 2019-01-05 + */ + +#ifndef _ELOG_FILE_CFG_H_ +#define _ELOG_FILE_CFG_H_ + +/* EasyLogger file log plugin's using file name */ +#define ELOG_FILE_NAME "elog_file.log" + +/* EasyLogger file log plugin's using file max size */ +#define ELOG_FILE_MAX_SIZE (1 * 1024 * 1024) + +/* EasyLogger file log plugin's using max rotate file count */ +#define ELOG_FILE_MAX_ROTATE 4 + +#endif /* _ELOG_FILE_CFG_H_ */ diff --git a/src/EasyLogger/src/elog.c b/src/EasyLogger/src/elog.c new file mode 100644 index 0000000..c4be412 --- /dev/null +++ b/src/EasyLogger/src/elog.c @@ -0,0 +1,873 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2018, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Initialize function and other general function. + * Created on: 2015-04-28 + */ + +#define LOG_TAG "elog" + +#include +#include +#include +#include + +#if !defined(ELOG_OUTPUT_LVL) + #error "Please configure static output log level (in elog_cfg.h)" +#endif + +#if !defined(ELOG_LINE_NUM_MAX_LEN) + #error "Please configure output line number max length (in elog_cfg.h)" +#endif + +#if !defined(ELOG_LINE_BUF_SIZE) + #error "Please configure buffer size for every line's log (in elog_cfg.h)" +#endif + +#if !defined(ELOG_FILTER_TAG_MAX_LEN) + #error "Please configure output filter's tag max length (in elog_cfg.h)" +#endif + +#if !defined(ELOG_FILTER_KW_MAX_LEN) + #error "Please configure output filter's keyword max length (in elog_cfg.h)" +#endif + +#if !defined(ELOG_NEWLINE_SIGN) + #error "Please configure output newline sign (in elog_cfg.h)" +#endif + +/* output filter's tag level max num */ +#ifndef ELOG_FILTER_TAG_LVL_MAX_NUM +#define ELOG_FILTER_TAG_LVL_MAX_NUM 4 +#endif + +#ifdef ELOG_COLOR_ENABLE +/** + * CSI(Control Sequence Introducer/Initiator) sign + * more information on https://en.wikipedia.org/wiki/ANSI_escape_code + */ +#define CSI_START "\033[" +#define CSI_END "\033[0m" +/* output log front color */ +#define F_BLACK "30;" +#define F_RED "31;" +#define F_GREEN "32;" +#define F_YELLOW "33;" +#define F_BLUE "34;" +#define F_MAGENTA "35;" +#define F_CYAN "36;" +#define F_WHITE "37;" +/* output log background color */ +#define B_NULL +#define B_BLACK "40;" +#define B_RED "41;" +#define B_GREEN "42;" +#define B_YELLOW "43;" +#define B_BLUE "44;" +#define B_MAGENTA "45;" +#define B_CYAN "46;" +#define B_WHITE "47;" +/* output log fonts style */ +#define S_BOLD "1m" +#define S_UNDERLINE "4m" +#define S_BLINK "5m" +#define S_NORMAL "22m" +/* output log default color definition: [front color] + [background color] + [show style] */ +#ifndef ELOG_COLOR_ASSERT +#define ELOG_COLOR_ASSERT (F_MAGENTA B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_ERROR +#define ELOG_COLOR_ERROR (F_RED B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_WARN +#define ELOG_COLOR_WARN (F_YELLOW B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_INFO +#define ELOG_COLOR_INFO (F_CYAN B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_DEBUG +#define ELOG_COLOR_DEBUG (F_GREEN B_NULL S_NORMAL) +#endif +#ifndef ELOG_COLOR_VERBOSE +#define ELOG_COLOR_VERBOSE (F_BLUE B_NULL S_NORMAL) +#endif +#endif /* ELOG_COLOR_ENABLE */ + +/* EasyLogger object */ +static EasyLogger elog; +/* every line log's buffer */ +static char log_buf[ELOG_LINE_BUF_SIZE] = { 0 }; +/* level output info */ +static const char *level_output_info[] = { + [ELOG_LVL_ASSERT] = "A/", + [ELOG_LVL_ERROR] = "E/", + [ELOG_LVL_WARN] = "W/", + [ELOG_LVL_INFO] = "I/", + [ELOG_LVL_DEBUG] = "D/", + [ELOG_LVL_VERBOSE] = "V/", +}; + +#ifdef ELOG_COLOR_ENABLE +/* color output info */ +static const char *color_output_info[] = { + [ELOG_LVL_ASSERT] = ELOG_COLOR_ASSERT, + [ELOG_LVL_ERROR] = ELOG_COLOR_ERROR, + [ELOG_LVL_WARN] = ELOG_COLOR_WARN, + [ELOG_LVL_INFO] = ELOG_COLOR_INFO, + [ELOG_LVL_DEBUG] = ELOG_COLOR_DEBUG, + [ELOG_LVL_VERBOSE] = ELOG_COLOR_VERBOSE, +}; +#endif /* ELOG_COLOR_ENABLE */ + +static bool get_fmt_enabled(uint8_t level, size_t set); +static void elog_set_filter_tag_lvl_default(); + +/* EasyLogger assert hook */ +void (*elog_assert_hook)(const char* expr, const char* func, size_t line); + +extern void elog_port_output(const char *log, size_t size); +extern void elog_port_output_lock(void); +extern void elog_port_output_unlock(void); + +/** + * EasyLogger initialize. + * + * @return result + */ +ElogErrCode elog_init(void) { + extern ElogErrCode elog_port_init(void); + extern ElogErrCode elog_async_init(void); + + ElogErrCode result = ELOG_NO_ERR; + + if (elog.init_ok == true) { + return result; + } + + /* port initialize */ + result = elog_port_init(); + if (result != ELOG_NO_ERR) { + return result; + } + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE + result = elog_async_init(); + if (result != ELOG_NO_ERR) { + return result; + } +#endif + + /* enable the output lock */ + elog_output_lock_enabled(true); + /* output locked status initialize */ + elog.output_is_locked_before_enable = false; + elog.output_is_locked_before_disable = false; + +#ifdef ELOG_COLOR_ENABLE + /* disable text color by default */ + elog_set_text_color_enabled(false); +#endif + + /* set level is ELOG_LVL_VERBOSE */ + elog_set_filter_lvl(ELOG_LVL_VERBOSE); + + /* set tag_level to default val */ + elog_set_filter_tag_lvl_default(); + + elog.init_ok = true; + + return result; +} + +/** + * EasyLogger start after initialize. + */ +void elog_start(void) { + /* enable output */ + elog_set_output_enabled(true); + +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + elog_async_enabled(true); +#elif defined(ELOG_BUF_OUTPUT_ENABLE) + elog_buf_enabled(true); +#endif + + /* show version */ + //log_i("EasyLogger V%s is initialize success.", ELOG_SW_VERSION); + elog_raw("EasyLogger V%s is initialize success.\n", ELOG_SW_VERSION); +} + +/** + * set output enable or disable + * + * @param enabled TRUE: enable FALSE: disable + */ +void elog_set_output_enabled(bool enabled) { + ELOG_ASSERT((enabled == false) || (enabled == true)); + + elog.output_enabled = enabled; +} + +#ifdef ELOG_COLOR_ENABLE +/** + * set log text color enable or disable + * + * @param enabled TRUE: enable FALSE:disable + */ +void elog_set_text_color_enabled(bool enabled) { + elog.text_color_enabled = enabled; +} + +/** + * get log text color enable status + * + * @return enable or disable + */ +bool elog_get_text_color_enabled(void) { + return elog.text_color_enabled; +} +#endif /* ELOG_COLOR_ENABLE */ + +/** + * get output is enable or disable + * + * @return enable or disable + */ +bool elog_get_output_enabled(void) { + return elog.output_enabled; +} + +/** + * set log output format. only enable or disable + * + * @param level level + * @param set format set + */ +void elog_set_fmt(uint8_t level, size_t set) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + elog.enabled_fmt_set[level] = set; +} + +/** + * set log filter all parameter + * + * @param level level + * @param tag tag + * @param keyword keyword + */ +void elog_set_filter(uint8_t level, const char *tag, const char *keyword) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + elog_set_filter_lvl(level); + elog_set_filter_tag(tag); + elog_set_filter_kw(keyword); +} + +/** + * set log filter's level + * + * @param level level + */ +void elog_set_filter_lvl(uint8_t level) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + elog.filter.level = level; +} + +/** + * set log filter's tag + * + * @param tag tag + */ +void elog_set_filter_tag(const char *tag) { + strncpy(elog.filter.tag, tag, ELOG_FILTER_TAG_MAX_LEN); +} + +/** + * set log filter's keyword + * + * @param keyword keyword + */ +void elog_set_filter_kw(const char *keyword) { + strncpy(elog.filter.keyword, keyword, ELOG_FILTER_KW_MAX_LEN); +} + +/** + * lock output + */ +void elog_output_lock(void) { + if (elog.output_lock_enabled) { + elog_port_output_lock(); + elog.output_is_locked_before_disable = true; + } else { + elog.output_is_locked_before_enable = true; + } +} + +/** + * unlock output + */ +void elog_output_unlock(void) { + if (elog.output_lock_enabled) { + elog_port_output_unlock(); + elog.output_is_locked_before_disable = false; + } else { + elog.output_is_locked_before_enable = false; + } +} + +/** + * set log filter's tag level val to default + */ +static void elog_set_filter_tag_lvl_default() +{ + uint8_t i = 0; + + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1); + elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT; + elog.filter.tag_lvl[i].tag_use_flag = false; + } +} + +/** + * Set the filter's level by different tag. + * The log on this tag which level is less than it will stop output. + * + * example: + * // the example tag log enter silent mode + * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_SILENT); + * // the example tag log which level is less than INFO level will stop output + * elog_set_filter_tag_lvl("example", ELOG_LVL_INFO); + * // remove example tag's level filter, all level log will resume output + * elog_set_filter_tag_lvl("example", ELOG_FILTER_LVL_ALL); + * + * @param tag log tag + * @param level The filter level. When the level is ELOG_FILTER_LVL_SILENT, the log enter silent mode. + * When the level is ELOG_FILTER_LVL_ALL, it will remove this tag's level filer. + * Then all level log will resume output. + * + */ +void elog_set_filter_tag_lvl(const char *tag, uint8_t level) +{ + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + ELOG_ASSERT(tag != ((void *)0)); + uint8_t i = 0; + + if (!elog.init_ok) { + return; + } + + elog_port_output_lock(); + /* find the tag in arr */ + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + if (elog.filter.tag_lvl[i].tag_use_flag == true && + !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){ + break; + } + } + + if (i < ELOG_FILTER_TAG_LVL_MAX_NUM){ + /* find OK */ + if (level == ELOG_FILTER_LVL_ALL){ + /* remove current tag's level filter when input level is the lowest level */ + elog.filter.tag_lvl[i].tag_use_flag = false; + memset(elog.filter.tag_lvl[i].tag, '\0', ELOG_FILTER_TAG_MAX_LEN + 1); + elog.filter.tag_lvl[i].level = ELOG_FILTER_LVL_SILENT; + } else{ + elog.filter.tag_lvl[i].level = level; + } + } else{ + /* only add the new tag's level filer when level is not ELOG_FILTER_LVL_ALL */ + if (level != ELOG_FILTER_LVL_ALL){ + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + if (elog.filter.tag_lvl[i].tag_use_flag == false){ + strncpy(elog.filter.tag_lvl[i].tag, tag, ELOG_FILTER_TAG_MAX_LEN); + elog.filter.tag_lvl[i].level = level; + elog.filter.tag_lvl[i].tag_use_flag = true; + break; + } + } + } + } + elog_output_unlock(); +} + +/** + * get the level on tag's level filer + * + * @param tag tag + * + * @return It will return the lowest level when tag was not found. + * Other level will return when tag was found. + */ +uint8_t elog_get_filter_tag_lvl(const char *tag) +{ + ELOG_ASSERT(tag != ((void *)0)); + uint8_t i = 0; + uint8_t level = ELOG_FILTER_LVL_ALL; + + if (!elog.init_ok) { + return level; + } + + elog_port_output_lock(); + /* find the tag in arr */ + for (i =0; i< ELOG_FILTER_TAG_LVL_MAX_NUM; i++){ + if (elog.filter.tag_lvl[i].tag_use_flag == true && + !strncmp(tag, elog.filter.tag_lvl[i].tag,ELOG_FILTER_TAG_MAX_LEN)){ + level = elog.filter.tag_lvl[i].level; + break; + } + } + elog_output_unlock(); + + return level; +} + +/** + * output RAW format log + * + * @param format output format + * @param ... args + */ +void elog_raw(const char *format, ...) { + va_list args; + size_t log_len = 0; + int fmt_result; + + /* check output enabled */ + if (!elog.output_enabled) { + return; + } + + /* args point to the first variable parameter */ + va_start(args, format); + + /* lock output */ + elog_output_lock(); + + /* package log data to buffer */ + fmt_result = vsnprintf(log_buf, ELOG_LINE_BUF_SIZE, format, args); + + /* output converted log */ + if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) { + log_len = fmt_result; + } else { + log_len = ELOG_LINE_BUF_SIZE; + } + /* output log */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(uint8_t level, const char *log, size_t size); + /* raw log will using assert level */ + elog_async_output(ELOG_LVL_ASSERT, log_buf, log_len); +#elif defined(ELOG_BUF_OUTPUT_ENABLE) + extern void elog_buf_output(const char *log, size_t size); + elog_buf_output(log_buf, log_len); +#else + elog_port_output(log_buf, log_len); +#endif + /* unlock output */ + elog_output_unlock(); + + va_end(args); +} + +/** + * output the log + * + * @param level level + * @param tag tag + * @param file file name + * @param func function name + * @param line line number + * @param format output format + * @param ... args + * + */ +void elog_output(uint8_t level, const char *tag, const char *file, const char *func, + const long line, const char *format, ...) { + extern const char *elog_port_get_time(void); + extern const char *elog_port_get_p_info(void); + extern const char *elog_port_get_t_info(void); + + size_t tag_len = strlen(tag), log_len = 0, newline_len = strlen(ELOG_NEWLINE_SIGN); + char line_num[ELOG_LINE_NUM_MAX_LEN + 1] = { 0 }; + char tag_sapce[ELOG_FILTER_TAG_MAX_LEN / 2 + 1] = { 0 }; + va_list args; + int fmt_result; + + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + /* check output enabled */ + if (!elog.output_enabled) { + return; + } + /* level filter */ + if (level > elog.filter.level || level > elog_get_filter_tag_lvl(tag)) { + return; + } else if (!strstr(tag, elog.filter.tag)) { /* tag filter */ + return; + } + /* args point to the first variable parameter */ + va_start(args, format); + /* lock output */ + elog_output_lock(); + +#ifdef ELOG_COLOR_ENABLE + /* add CSI start sign and color info */ + if (elog.text_color_enabled) { + log_len += elog_strcpy(log_len, log_buf + log_len, CSI_START); + log_len += elog_strcpy(log_len, log_buf + log_len, color_output_info[level]); + } +#endif + + /* package level info */ + if (get_fmt_enabled(level, ELOG_FMT_LVL)) { + log_len += elog_strcpy(log_len, log_buf + log_len, level_output_info[level]); + } + /* package tag info */ + if (get_fmt_enabled(level, ELOG_FMT_TAG)) { + log_len += elog_strcpy(log_len, log_buf + log_len, tag); + /* if the tag length is less than 50% ELOG_FILTER_TAG_MAX_LEN, then fill space */ + if (tag_len <= ELOG_FILTER_TAG_MAX_LEN / 2) { + memset(tag_sapce, ' ', ELOG_FILTER_TAG_MAX_LEN / 2 - tag_len); + log_len += elog_strcpy(log_len, log_buf + log_len, tag_sapce); + } + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + /* package time, process and thread info */ + if (get_fmt_enabled(level, ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, "["); + /* package time info */ + if (get_fmt_enabled(level, ELOG_FMT_TIME)) { + log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_time()); + if (get_fmt_enabled(level, ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + /* package process info */ + if (get_fmt_enabled(level, ELOG_FMT_P_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_p_info()); + if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + /* package thread info */ + if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) { + log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_t_info()); + } + log_len += elog_strcpy(log_len, log_buf + log_len, "] "); + } + /* package file directory and name, function name and line number info */ + if (get_fmt_enabled(level, ELOG_FMT_DIR | ELOG_FMT_FUNC | ELOG_FMT_LINE)) { + log_len += elog_strcpy(log_len, log_buf + log_len, "("); + /* package time info */ + if (get_fmt_enabled(level, ELOG_FMT_DIR)) { + log_len += elog_strcpy(log_len, log_buf + log_len, file); + if (get_fmt_enabled(level, ELOG_FMT_FUNC)) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } else if (get_fmt_enabled(level, ELOG_FMT_LINE)) { + log_len += elog_strcpy(log_len, log_buf + log_len, ":"); + } + } + /* package process info */ + if (get_fmt_enabled(level, ELOG_FMT_FUNC)) { + log_len += elog_strcpy(log_len, log_buf + log_len, func); + if (get_fmt_enabled(level, ELOG_FMT_LINE)) { + log_len += elog_strcpy(log_len, log_buf + log_len, ":"); + } + } + /* package thread info */ + if (get_fmt_enabled(level, ELOG_FMT_LINE)) { + snprintf(line_num, ELOG_LINE_NUM_MAX_LEN, "%ld", line); + log_len += elog_strcpy(log_len, log_buf + log_len, line_num); + } + log_len += elog_strcpy(log_len, log_buf + log_len, ")"); + } + /* package other log data to buffer. '\0' must be added in the end by vsnprintf. */ + fmt_result = vsnprintf(log_buf + log_len, ELOG_LINE_BUF_SIZE - log_len, format, args); + + va_end(args); + /* calculate log length */ + if ((log_len + fmt_result <= ELOG_LINE_BUF_SIZE) && (fmt_result > -1)) { + log_len += fmt_result; + } else { + /* using max length */ + log_len = ELOG_LINE_BUF_SIZE; + } + /* overflow check and reserve some space for CSI end sign and newline sign */ +#ifdef ELOG_COLOR_ENABLE + if (log_len + (sizeof(CSI_END) - 1) + newline_len > ELOG_LINE_BUF_SIZE) { + /* using max length */ + log_len = ELOG_LINE_BUF_SIZE; + /* reserve some space for CSI end sign */ + log_len -= (sizeof(CSI_END) - 1); +#else + if (log_len + newline_len > ELOG_LINE_BUF_SIZE) { + /* using max length */ + log_len = ELOG_LINE_BUF_SIZE; +#endif /* ELOG_COLOR_ENABLE */ + /* reserve some space for newline sign */ + log_len -= newline_len; + } + /* keyword filter */ + if (elog.filter.keyword[0] != '\0') { + /* add string end sign */ + log_buf[log_len] = '\0'; + /* find the keyword */ + if (!strstr(log_buf, elog.filter.keyword)) { + /* unlock output */ + elog_output_unlock(); + return; + } + } + +#ifdef ELOG_COLOR_ENABLE + /* add CSI end sign */ + if (elog.text_color_enabled) { + log_len += elog_strcpy(log_len, log_buf + log_len, CSI_END); + } +#endif + + /* package newline sign */ + log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN); + /* output log */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(uint8_t level, const char *log, size_t size); + elog_async_output(level, log_buf, log_len); +#elif defined(ELOG_BUF_OUTPUT_ENABLE) + extern void elog_buf_output(const char *log, size_t size); + elog_buf_output(log_buf, log_len); +#else + elog_port_output(log_buf, log_len); +#endif + /* unlock output */ + elog_output_unlock(); +} + +/** + * get format enabled + * + * @param level level + * @param set format set + * + * @return enable or disable + */ +static bool get_fmt_enabled(uint8_t level, size_t set) { + ELOG_ASSERT(level <= ELOG_LVL_VERBOSE); + + if (elog.enabled_fmt_set[level] & set) { + return true; + } else { + return false; + } +} + +/** + * enable or disable logger output lock + * @note disable this lock is not recommended except you want output system exception log + * + * @param enabled true: enable false: disable + */ +void elog_output_lock_enabled(bool enabled) { + elog.output_lock_enabled = enabled; + /* it will re-lock or re-unlock before output lock enable */ + if (elog.output_lock_enabled) { + if (!elog.output_is_locked_before_disable && elog.output_is_locked_before_enable) { + /* the output lock is unlocked before disable, and the lock will unlocking after enable */ + elog_port_output_lock(); + } else if (elog.output_is_locked_before_disable && !elog.output_is_locked_before_enable) { + /* the output lock is locked before disable, and the lock will locking after enable */ + elog_port_output_unlock(); + } + } +} + +/** + * Set a hook function to EasyLogger assert. It will run when the expression is false. + * + * @param hook the hook function + */ +void elog_assert_set_hook(void (*hook)(const char* expr, const char* func, size_t line)) { + elog_assert_hook = hook; +} + +/** + * find the log level + * @note make sure the log level is output on each format + * + * @param log log buffer + * + * @return log level, found failed will return -1 + */ +int8_t elog_find_lvl(const char *log) { + ELOG_ASSERT(log); + /* make sure the log level is output on each format */ + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_ASSERT] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_ERROR] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_WARN] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_INFO] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_DEBUG] & ELOG_FMT_LVL); + ELOG_ASSERT(elog.enabled_fmt_set[ELOG_LVL_VERBOSE] & ELOG_FMT_LVL); + +#ifdef ELOG_COLOR_ENABLE + uint8_t i; + size_t csi_start_len = strlen(CSI_START); + for(i = 0; i < ELOG_LVL_TOTAL_NUM; i ++) { + if (!strncmp(color_output_info[i], log + csi_start_len, strlen(color_output_info[i]))) { + return i; + } + } + /* found failed */ + return -1; +#else + switch (log[0]) { + case 'A': return ELOG_LVL_ASSERT; + case 'E': return ELOG_LVL_ERROR; + case 'W': return ELOG_LVL_WARN; + case 'I': return ELOG_LVL_INFO; + case 'D': return ELOG_LVL_DEBUG; + case 'V': return ELOG_LVL_VERBOSE; + default: return -1; + } +#endif +} + +/** + * find the log tag + * @note make sure the log tag is output on each format + * @note the tag don't have space in it + * + * @param log log buffer + * @param lvl log level, you can get it by @see elog_find_lvl + * @param tag_len found tag length + * + * @return log tag, found failed will return NULL + */ +const char *elog_find_tag(const char *log, uint8_t lvl, size_t *tag_len) { + const char *tag = NULL, *tag_end = NULL; + + ELOG_ASSERT(log); + ELOG_ASSERT(tag_len); + ELOG_ASSERT(lvl < ELOG_LVL_TOTAL_NUM); + /* make sure the log tag is output on each format */ + ELOG_ASSERT(elog.enabled_fmt_set[lvl] & ELOG_FMT_TAG); + +#ifdef ELOG_COLOR_ENABLE + tag = log + strlen(CSI_START) + strlen(color_output_info[lvl]) + strlen(level_output_info[lvl]); +#else + tag = log + strlen(level_output_info[lvl]); +#endif + /* find the first space after tag */ + if ((tag_end = memchr(tag, ' ', ELOG_FILTER_TAG_MAX_LEN)) != NULL) { + *tag_len = tag_end - tag; + } else { + tag = NULL; + } + + return tag; +} + +/** + * dump the hex format data to log + * + * @param name name for hex object, it will show on log header + * @param width hex number for every line, such as: 16, 32 + * @param buf hex buffer + * @param size buffer size + */ +void elog_hexdump(const char *name, uint8_t width, uint8_t *buf, uint16_t size) +{ +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') + + uint16_t i, j; + uint16_t log_len = 0; + char dump_string[8] = {0}; + int fmt_result; + + if (!elog.output_enabled) { + return; + } + + /* level filter */ + if (ELOG_LVL_DEBUG > elog.filter.level) { + return; + } else if (!strstr(name, elog.filter.tag)) { /* tag filter */ + return; + } + + /* lock output */ + elog_output_lock(); + + for (i = 0; i < size; i += width) { + /* package header */ + fmt_result = snprintf(log_buf, ELOG_LINE_BUF_SIZE, "D/HEX %s: %04X-%04X: ", name, i, i + width - 1); + /* calculate log length */ + if ((fmt_result > -1) && (fmt_result <= ELOG_LINE_BUF_SIZE)) { + log_len = fmt_result; + } else { + log_len = ELOG_LINE_BUF_SIZE; + } + /* dump hex */ + for (j = 0; j < width; j++) { + if (i + j < size) { + snprintf(dump_string, sizeof(dump_string), "%02X ", buf[i + j]); + } else { + strncpy(dump_string, " ", sizeof(dump_string)); + } + log_len += elog_strcpy(log_len, log_buf + log_len, dump_string); + if ((j + 1) % 8 == 0) { + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + } + } + log_len += elog_strcpy(log_len, log_buf + log_len, " "); + /* dump char for hex */ + for (j = 0; j < width; j++) { + if (i + j < size) { + snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); + log_len += elog_strcpy(log_len, log_buf + log_len, dump_string); + } + } + /* overflow check and reserve some space for newline sign */ + if (log_len + strlen(ELOG_NEWLINE_SIGN) > ELOG_LINE_BUF_SIZE) { + log_len = ELOG_LINE_BUF_SIZE - strlen(ELOG_NEWLINE_SIGN); + } + /* package newline sign */ + log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN); + /* do log output */ +#if defined(ELOG_ASYNC_OUTPUT_ENABLE) + extern void elog_async_output(uint8_t level, const char *log, size_t size); + elog_async_output(ELOG_LVL_DEBUG, log_buf, log_len); +#elif defined(ELOG_BUF_OUTPUT_ENABLE) + extern void elog_buf_output(const char *log, size_t size); + elog_buf_output(log_buf, log_len); +#else + elog_port_output(log_buf, log_len); +#endif + } + /* unlock output */ + elog_output_unlock(); +} diff --git a/src/EasyLogger/src/elog_async.c b/src/EasyLogger/src/elog_async.c new file mode 100644 index 0000000..cade4bf --- /dev/null +++ b/src/EasyLogger/src/elog_async.c @@ -0,0 +1,352 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2016-2017, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Logs asynchronous output. + * Created on: 2016-11-06 + */ + +#include +#include + +#ifdef ELOG_ASYNC_OUTPUT_ENABLE + +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD +#include +#include +#include +/* thread default stack size */ +#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE +#if PTHREAD_STACK_MIN > 4*1024 +#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE PTHREAD_STACK_MIN +#else +#define ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE (1*1024) +#endif +/* thread default priority */ +#ifndef ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY +#define ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY (sched_get_priority_max(SCHED_RR) - 1) +#endif +/* output thread poll get log buffer size */ +#ifndef ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE +#define ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE (ELOG_LINE_BUF_SIZE - 4) +#endif +#endif /* ELOG_ASYNC_OUTPUT_USING_PTHREAD */ + +/* asynchronous output log notice */ +static sem_t output_notice; +/* asynchronous output pthread thread */ +static pthread_t async_output_thread; +#endif /* ELOG_ASYNC_OUTPUT_USING_PTHREAD */ + +/* the highest output level for async mode, other level will sync output */ +#ifdef ELOG_ASYNC_OUTPUT_LVL +#define OUTPUT_LVL ELOG_ASYNC_OUTPUT_LVL +#else +#define OUTPUT_LVL ELOG_LVL_ASSERT +#endif /* ELOG_ASYNC_OUTPUT_LVL */ + +/* buffer size for asynchronous output mode */ +#ifdef ELOG_ASYNC_OUTPUT_BUF_SIZE +#define OUTPUT_BUF_SIZE ELOG_ASYNC_OUTPUT_BUF_SIZE +#else +#define OUTPUT_BUF_SIZE (ELOG_LINE_BUF_SIZE * 10) +#endif /* ELOG_ASYNC_OUTPUT_BUF_SIZE */ + +/* Initialize OK flag */ +static bool init_ok = false; +/* asynchronous output mode enabled flag */ +static bool is_enabled = false; +/* asynchronous output mode's ring buffer */ +static char log_buf[OUTPUT_BUF_SIZE] = { 0 }; +/* log ring buffer write index */ +static size_t write_index = 0; +/* log ring buffer read index */ +static size_t read_index = 0; +/* log ring buffer full flag */ +static bool buf_is_full = false; +/* log ring buffer empty flag */ +static bool buf_is_empty = true; + +extern void elog_port_output(const char *log, size_t size); +extern void elog_output_lock(void); +extern void elog_output_unlock(void); + +/** + * asynchronous output ring buffer used size + * + * @return used size + */ +static size_t elog_async_get_buf_used(void) { + if (write_index > read_index) { + return write_index - read_index; + } else { + if (!buf_is_full && !buf_is_empty) { + return OUTPUT_BUF_SIZE - (read_index - write_index); + } else if (buf_is_full) { + return OUTPUT_BUF_SIZE; + } else { + return 0; + } + } +} + +/** + * asynchronous output ring buffer remain space + * + * @return remain space + */ +static size_t async_get_buf_space(void) { + return OUTPUT_BUF_SIZE - elog_async_get_buf_used(); +} + +/** + * put log to asynchronous output ring buffer + * + * @param log put log buffer + * @param size log size + * + * @return put log size, the log which beyond ring buffer space will be dropped + */ +static size_t async_put_log(const char *log, size_t size) { + size_t space = 0; + + space = async_get_buf_space(); + /* no space */ + if (!space) { + size = 0; + goto __exit; + } + /* drop some log */ + if (space <= size) { + size = space; + buf_is_full = true; + } + + if (write_index + size < OUTPUT_BUF_SIZE) { + memcpy(log_buf + write_index, log, size); + write_index += size; + } else { + memcpy(log_buf + write_index, log, OUTPUT_BUF_SIZE - write_index); + memcpy(log_buf, log + OUTPUT_BUF_SIZE - write_index, + size - (OUTPUT_BUF_SIZE - write_index)); + write_index += size - OUTPUT_BUF_SIZE; + } + + buf_is_empty = false; + +__exit: + + return size; +} + +#ifdef ELOG_ASYNC_LINE_OUTPUT +/** + * Get line log from asynchronous output ring buffer. + * It will copy all log when the newline sign isn't find. + * + * @param log get line log buffer + * @param size line log size + * + * @return get line log size, the log size is less than ring buffer used size + */ +size_t elog_async_get_line_log(char *log, size_t size) { + size_t used = 0, cpy_log_size = 0; + /* lock output */ + elog_output_lock(); + used = elog_async_get_buf_used(); + + /* no log */ + if (!used || !size) { + goto __exit; + } + /* less log */ + if (used <= size) { + size = used; + } + + if (read_index + size < OUTPUT_BUF_SIZE) { + cpy_log_size = elog_cpyln(log, log_buf + read_index, size); + read_index += cpy_log_size; + } else { + cpy_log_size = elog_cpyln(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index); + if (cpy_log_size == OUTPUT_BUF_SIZE - read_index) { + cpy_log_size += elog_cpyln(log + cpy_log_size, log_buf, size - cpy_log_size); + read_index += cpy_log_size - OUTPUT_BUF_SIZE; + } else { + read_index += cpy_log_size; + } + } + + if (used == cpy_log_size) { + buf_is_empty = true; + } + + if (cpy_log_size) { + buf_is_full = false; + } + +__exit: + /* lock output */ + elog_output_unlock(); + return cpy_log_size; +} +#else +/** + * get log from asynchronous output ring buffer + * + * @param log get log buffer + * @param size log size + * + * @return get log size, the log size is less than ring buffer used size + */ +size_t elog_async_get_log(char *log, size_t size) { + size_t used = 0; + /* lock output */ + elog_output_lock(); + used = elog_async_get_buf_used(); + /* no log */ + if (!used || !size) { + size = 0; + goto __exit; + } + /* less log */ + if (used <= size) { + size = used; + buf_is_empty = true; + } + + if (read_index + size < OUTPUT_BUF_SIZE) { + memcpy(log, log_buf + read_index, size); + read_index += size; + } else { + memcpy(log, log_buf + read_index, OUTPUT_BUF_SIZE - read_index); + memcpy(log + OUTPUT_BUF_SIZE - read_index, log_buf, + size - (OUTPUT_BUF_SIZE - read_index)); + read_index += size - OUTPUT_BUF_SIZE; + } + + buf_is_full = false; + +__exit: + /* lock output */ + elog_output_unlock(); + return size; +} +#endif /* ELOG_ASYNC_LINE_OUTPUT */ + +void elog_async_output(uint8_t level, const char *log, size_t size) { + /* this function must be implement by user when ELOG_ASYNC_OUTPUT_USING_PTHREAD is not defined */ + extern void elog_async_output_notice(void); + size_t put_size; + + if (is_enabled) { + if (level >= OUTPUT_LVL) { + put_size = async_put_log(log, size); + /* notify output log thread */ + if (put_size > 0) { + elog_async_output_notice(); + } + } else { + elog_port_output(log, size); + } + } else { + elog_port_output(log, size); + } +} + +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD +void elog_async_output_notice(void) { + sem_post(&output_notice); +} + +static void *async_output(void *arg) { + size_t get_log_size = 0; + static char poll_get_buf[ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE]; + + while(true) { + /* waiting log */ + sem_wait(&output_notice); + /* polling gets and outputs the log */ + while(true) { + +#ifdef ELOG_ASYNC_LINE_OUTPUT + get_log_size = elog_async_get_line_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE); +#else + get_log_size = elog_async_get_log(poll_get_buf, ELOG_ASYNC_POLL_GET_LOG_BUF_SIZE); +#endif + + if (get_log_size) { + elog_port_output(poll_get_buf, get_log_size); + } else { + break; + } + } + } + return NULL; +} +#endif + +/** + * enable or disable asynchronous output mode + * the log will be output directly when mode is disabled + * + * @param enabled true: enabled, false: disabled + */ +void elog_async_enabled(bool enabled) { + is_enabled = enabled; +} + +/** + * asynchronous output mode initialize + * + * @return result + */ +ElogErrCode elog_async_init(void) { + ElogErrCode result = ELOG_NO_ERR; + + if (init_ok) { + return result; + } + +#ifdef ELOG_ASYNC_OUTPUT_USING_PTHREAD + pthread_attr_t thread_attr; + struct sched_param thread_sched_param; + + sem_init(&output_notice, 0, 0); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&thread_attr, ELOG_ASYNC_OUTPUT_PTHREAD_STACK_SIZE); + pthread_attr_setschedpolicy(&thread_attr, SCHED_RR); + thread_sched_param.sched_priority = ELOG_ASYNC_OUTPUT_PTHREAD_PRIORITY; + pthread_attr_setschedparam(&thread_attr, &thread_sched_param); + pthread_create(&async_output_thread, &thread_attr, async_output, NULL); + pthread_attr_destroy(&thread_attr); +#endif + + init_ok = true; + + return result; +} + +#endif /* ELOG_ASYNC_OUTPUT_ENABLE */ diff --git a/src/EasyLogger/src/elog_buf.c b/src/EasyLogger/src/elog_buf.c new file mode 100644 index 0000000..b4a8f10 --- /dev/null +++ b/src/EasyLogger/src/elog_buf.c @@ -0,0 +1,104 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2016, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Logs buffered output. + * Created on: 2016-11-09 + */ + +#include +#include + +#ifdef ELOG_BUF_OUTPUT_ENABLE +#if !defined(ELOG_BUF_OUTPUT_BUF_SIZE) + #error "Please configure buffer size for buffered output mode (in elog_cfg.h)" +#endif + +/* buffered output mode's buffer */ +static char log_buf[ELOG_BUF_OUTPUT_BUF_SIZE] = { 0 }; +/* log buffer current write size */ +static size_t buf_write_size = 0; +/* buffered output mode enabled flag */ +static bool is_enabled = false; + +extern void elog_port_output(const char *log, size_t size); +extern void elog_output_lock(void); +extern void elog_output_unlock(void); + +/** + * output buffered logs when buffer is full + * + * @param log will be buffered line's log + * @param size log size + */ +void elog_buf_output(const char *log, size_t size) { + size_t write_size = 0, write_index = 0; + + if (!is_enabled) { + elog_port_output(log, size); + return; + } + + while (true) { + if (buf_write_size + size > ELOG_BUF_OUTPUT_BUF_SIZE) { + write_size = ELOG_BUF_OUTPUT_BUF_SIZE - buf_write_size; + memcpy(log_buf + buf_write_size, log + write_index, write_size); + write_index += write_size; + size -= write_size; + buf_write_size += write_size; + /* output log */ + elog_port_output(log_buf, buf_write_size); + /* reset write index */ + buf_write_size = 0; + } else { + memcpy(log_buf + buf_write_size, log + write_index, size); + buf_write_size += size; + break; + } + } +} + +/** + * flush all buffered logs to output device + */ +void elog_flush(void) { + /* lock output */ + elog_output_lock(); + /* output log */ + elog_port_output(log_buf, buf_write_size); + /* reset write index */ + buf_write_size = 0; + /* unlock output */ + elog_output_unlock(); +} + +/** + * enable or disable buffered output mode + * the log will be output directly when mode is disabled + * + * @param enabled true: enabled, false: disabled + */ +void elog_buf_enabled(bool enabled) { + is_enabled = enabled; +} +#endif /* ELOG_BUF_OUTPUT_ENABLE */ diff --git a/src/EasyLogger/src/elog_file.c b/src/EasyLogger/src/elog_file.c new file mode 100644 index 0000000..e990db8 --- /dev/null +++ b/src/EasyLogger/src/elog_file.c @@ -0,0 +1,165 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2019, Qintl, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Save log to file. + * Created on: 2019-01-05 + */ + + #define LOG_TAG "elog.file" + +#include +#include +#include +#include + +#include "elog_file.h" + +/* initialize OK flag */ +static bool init_ok = false; +static FILE *fp = NULL; +static ElogFileCfg local_cfg; + +ElogErrCode elog_file_init(void) +{ + ElogErrCode result = ELOG_NO_ERR; + ElogFileCfg cfg; + + if (init_ok) + goto __exit; + + elog_file_port_init(); + + cfg.name = ELOG_FILE_NAME; + cfg.max_size = ELOG_FILE_MAX_SIZE; + cfg.max_rotate = ELOG_FILE_MAX_ROTATE; + + elog_file_config(&cfg); + + init_ok = true; +__exit: + return result; +} + +/* + * rotate the log file xxx.log.n-1 => xxx.log.n, and xxx.log => xxx.log.0 + */ +static bool elog_file_rotate(void) +{ +#define SUFFIX_LEN 10 + /* mv xxx.log.n-1 => xxx.log.n, and xxx.log => xxx.log.0 */ + int n, err = 0; + char oldpath[256], newpath[256]; + size_t base = strlen(local_cfg.name); + bool result = true; + FILE *tmp_fp; + + memcpy(oldpath, local_cfg.name, base); + memcpy(newpath, local_cfg.name, base); + + fclose(fp); + + for (n = local_cfg.max_rotate - 1; n >= 0; --n) { + snprintf(oldpath + base, SUFFIX_LEN, n ? ".%d" : "", n - 1); + snprintf(newpath + base, SUFFIX_LEN, ".%d", n); + /* remove the old file */ + if ((tmp_fp = fopen(newpath , "r")) != NULL) { + fclose(tmp_fp); + remove(newpath); + } + /* change the new log file to old file name */ + if ((tmp_fp = fopen(oldpath , "r")) != NULL) { + fclose(tmp_fp); + err = rename(oldpath, newpath); + } + + if (err < 0) { + result = false; + goto __exit; + } + } + +__exit: + /* reopen the file */ + fp = fopen(local_cfg.name, "a+"); + + return result; +} + + +void elog_file_write(const char *log, size_t size) +{ + size_t file_size = 0; + + ELOG_ASSERT(init_ok); + ELOG_ASSERT(log); + + elog_file_port_lock(); + + fseek(fp, 0L, SEEK_END); + file_size = ftell(fp); + + if (unlikely(file_size > local_cfg.max_size)) { +#if ELOG_FILE_MAX_ROTATE > 0 + if (!elog_file_rotate()) { + goto __exit; + } +#else + goto __exit; +#endif + } + + fwrite(log, size, 1, fp); + +#ifdef ELOG_FILE_FLUSH_CAHCE_ENABLE + fflush(fp); +#endif + +__exit: + elog_file_port_unlock(); +} + +void elog_file_deinit(void) +{ + ELOG_ASSERT(init_ok); + + elog_file_port_deinit(); + fclose(fp); +} + +void elog_file_config(ElogFileCfg *cfg) +{ + if (fp) { + fclose(fp); + } + + elog_file_port_lock(); + + local_cfg.name = cfg->name; + local_cfg.max_size = cfg->max_size; + local_cfg.max_rotate = cfg->max_rotate; + + fp = fopen(local_cfg.name, "a+"); + + elog_file_port_unlock(); +} diff --git a/src/EasyLogger/src/elog_file_port.c b/src/EasyLogger/src/elog_file_port.c new file mode 100644 index 0000000..a08347e --- /dev/null +++ b/src/EasyLogger/src/elog_file_port.c @@ -0,0 +1,160 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2019, Qintl, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Portable interface for EasyLogger's file log pulgin. + * Created on: 2019-01-05 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define ELOG_FILE_SEM_KEY ((key_t)0x19910612) +#ifdef _SEM_SEMUN_UNDEFINED +union semun { + int val; /* Value for SETVAL */ + struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* Array for GETALL, SETALL */ + struct seminfo *__buf; /* Buffer for IPC_INFO + (Linux-specific) */ +}; +#endif + +static int semid = -1; +static struct sembuf const up = {0, 1, SEM_UNDO}; +static struct sembuf const down = {0, -1, SEM_UNDO}; + +static void lock_init(void); +static int lock_open(void); +/** + * EasyLogger flile log pulgin port initialize + * + * @return result + */ +ElogErrCode elog_file_port_init(void) { + ElogErrCode result = ELOG_NO_ERR; + + lock_init(); + + return result; +} + +/** + * file log lock + */ +void inline elog_file_port_lock(void) +{ + semid == -1 ? -1 : semop(semid, (struct sembuf *)&down, 1); +} + +/** + * file log unlock + */ +void inline elog_file_port_unlock(void) +{ + semid == -1 ? -1 : semop(semid, (struct sembuf *)&up, 1); +} +/** + * file log deinit + */ +void elog_file_port_deinit(void) +{ + +} + +/** + * initialize the lock + */ +static void lock_init(void) +{ + int id, rc; + union semun arg; + struct sembuf sembuf; + + id = semget(ELOG_FILE_SEM_KEY, 1, IPC_CREAT | IPC_EXCL | 0666); + if(likely(id == -1)) { + id = lock_open(); + if (id == -1) + goto __exit; + } else { + arg.val = 0; + rc = semctl(id, 0, SETVAL, arg); + if (rc == -1) + goto __exit; + + sembuf.sem_num = 0; + sembuf.sem_op = 1; + sembuf.sem_flg = 0; + + rc = semop(id, &sembuf, 1); + if (rc == -1) + goto __exit; + } + + semid = id; +__exit: + return ; +} + +/** + * gets the lock + */ +static int lock_open(void) +{ + int id, rc, i; + union semun arg; + struct semid_ds ds; + + id = semget(ELOG_FILE_SEM_KEY, 1, 0666); + if(unlikely(id == -1)) + goto err; + + arg.buf = &ds; + + for (i = 0; i < 10; i++) { + rc = semctl(id, 0, IPC_STAT, arg); + if (unlikely(rc == -1)) + goto err; + + if(ds.sem_otime != 0) + break; + + usleep(10 * 1000); + } + + if (unlikely(ds.sem_otime == 0)) + goto err; + + return id; +err: + return -1; +} diff --git a/src/EasyLogger/src/elog_port.c b/src/EasyLogger/src/elog_port.c new file mode 100644 index 0000000..cde29bc --- /dev/null +++ b/src/EasyLogger/src/elog_port.c @@ -0,0 +1,132 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Portable interface for linux. + * Created on: 2015-04-28 + */ + +#include +#include +#include +#include +#include + +#ifdef ELOG_FILE_ENABLE +#include +#endif +static pthread_mutex_t output_lock; + +/** + * EasyLogger port initialize + * + * @return result + */ +ElogErrCode elog_port_init(void) { + ElogErrCode result = ELOG_NO_ERR; + + pthread_mutex_init(&output_lock, NULL); + +#ifdef ELOG_FILE_ENABLE + elog_file_init(); +#endif + + return result; +} + +/** + * output log port interface + * + * @param log output of log + * @param size log size + */ +void elog_port_output(const char *log, size_t size) { + /* output to terminal */ + printf("%.*s", (int)size, log); +#ifdef ELOG_FILE_ENABLE + /* write the file */ + elog_file_write(log, size); +#endif +} + +/** + * output lock + */ +void elog_port_output_lock(void) { + pthread_mutex_lock(&output_lock); +} + +/** + * output unlock + */ +void elog_port_output_unlock(void) { + pthread_mutex_unlock(&output_lock); +} + + +/** + * get current time interface + * + * @return current time + */ +const char *elog_port_get_time(void) { + static char cur_system_time[24] = { 0 }; + time_t timep; + struct tm *p; + + time(&timep); + p = localtime(&timep); + if (p == NULL) { + return ""; + } + snprintf(cur_system_time, 18, "%02d-%02d %02d:%02d:%02d", p->tm_mon + 1, p->tm_mday, + p->tm_hour, p->tm_min, p->tm_sec); + + return cur_system_time; +} + +/** + * get current process name interface + * + * @return current process name + */ +const char *elog_port_get_p_info(void) { + static char cur_process_info[10] = { 0 }; + + snprintf(cur_process_info, 10, "pid:%04d", getpid()); + + return cur_process_info; +} + +/** + * get current thread name interface + * + * @return current thread name + */ +const char *elog_port_get_t_info(void) { + static char cur_thread_info[10] = { 0 }; + + snprintf(cur_thread_info, 10, "tid:%04ld", pthread_self()); + + return cur_thread_info; +} diff --git a/src/EasyLogger/src/elog_utils.c b/src/EasyLogger/src/elog_utils.c new file mode 100644 index 0000000..165d855 --- /dev/null +++ b/src/EasyLogger/src/elog_utils.c @@ -0,0 +1,103 @@ +/* + * This file is part of the EasyLogger Library. + * + * Copyright (c) 2015-2018, Armink, + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Function: Some utils for this library. + * Created on: 2015-04-28 + */ + +#include +#include + +/** + * another copy string function + * + * @param cur_len current copied log length, max size is ELOG_LINE_BUF_SIZE + * @param dst destination + * @param src source + * + * @return copied length + */ +size_t elog_strcpy(size_t cur_len, char *dst, const char *src) { + const char *src_old = src; + + assert(dst); + assert(src); + + while (*src != 0) { + /* make sure destination has enough space */ + if (cur_len++ < ELOG_LINE_BUF_SIZE) { + *dst++ = *src++; + } else { + break; + } + } + return src - src_old; +} + +/** + * Copy line log split by newline sign. It will copy all log when the newline sign isn't find. + * + * @param line line log buffer + * @param log origin log buffer + * @param len origin log buffer length + * + * @return copy size + */ +size_t elog_cpyln(char *line, const char *log, size_t len) { + size_t newline_len = strlen(ELOG_NEWLINE_SIGN), copy_size = 0; + + assert(line); + assert(log); + + while (len--) { + *line++ = *log++; + copy_size++; + if (copy_size >= newline_len && !strncmp(log - newline_len, ELOG_NEWLINE_SIGN, newline_len)) { + break; + } + } + return copy_size; +} + +/** + * This function will copy memory content from source address to destination + * address. + * + * @param dst the address of destination memory + * @param src the address of source memory + * @param count the copied length + * + * @return the address of destination memory + */ +void *elog_memcpy(void *dst, const void *src, size_t count) { + char *tmp = (char *) dst, *s = (char *) src; + + assert(dst); + assert(src); + + while (count--) + *tmp++ = *s++; + + return dst; +} diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..77d1756 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,288 @@ +# Generic Makefile by tmb +# Program name +PROGRAM = sjzd_app + +#PLAT = x86 +PLAT = arm + +#VER = debug +VER = release + +TYPE = bin +#TYPE = so + +##========================================================================== +PROGRAM := $(strip $(PROGRAM)) +PLAY := $(strip $(TYPE)) +VER := $(strip $(VER)) + +MY_CFLAGS = -fmessage-length=0 + +ifeq ($(PLAT),x86) +MY_LIBS = -lpthread -L../lib -lrt -ldl -lscl_shm -lm -lgo +else +MY_LIBS = -L../lib -lpthread -lrt -ldl -lscl_shm -lm -lgo +endif + +# The pre-processor options used by the cpp (man cpp for more). +#CPPFLAGS = -DHAVE_CONFIG_H -Wall -Wextra -Wno-invalid-offsetof +CPPFLAGS = -Wall -Wextra + +# The options used in linking as well as in any direct use of ld. +ifeq ($(TYPE),so) +LDFLAGS = -shared -fPIC +else +LDFLAGS = +endif + +# The directories in which source files reside. +# If not specified, only the current directory will be serached. +SRCDIRS = + +## Implicit Section: change the following only when necessary. +##========================================================================== + +# The source file types (headers excluded). +# .c indicates C source files, and others C++ ones. +SRCEXTS = .c .C .cc .cpp .CPP .c++ .cxx .cp + +# The header file types. +HDREXTS = .h .H .hh .hpp .HPP .h++ .hxx .hp + +# The pre-processor and compiler options. +# Users can override those variables from the command line. +ifeq ($(VER),debug) +ifeq ($(TYPE),so) +CFLAGS = -ggdb -pipe -O0 -fPIC -I. -I../inc +CXXFLAGS= -ggdb -pipe -O0 -fPIC -I. -I../inc +else +CFLAGS = -ggdb -pipe -O0 -I. -I../inc +CXXFLAGS= -ggdb -pipe -O0 -I. -I../inc +endif +else +ifeq ($(TYPE),so) +CFLAGS = -O2 -pipe -fPIC -I. -I../inc +CXXFLAGS= -O2 -pipe -fPIC -I. -I../inc +else +CFLAGS = -O2 -pipe -I. -I../inc +CXXFLAGS= -O2 -pipe -I. -I../inc +endif +endif + +# The C program compiler. +ifeq ($(PLAT),x86) +CC = gcc +else +CC = arm-linux-gnueabihf-gcc +endif + +# The C++ program compiler. +ifeq ($(PLAT),x86) +CXX = g++ +else +CXX = arm-linux-gnueabihf-g++ +endif + +# Un-comment the following line to compile C programs as C++ ones. +#CC = $(CXX) + +ifeq ($(PLAT),x86) +STRIP = strip +else +STRIP = arm-none-linux-gnueabi-strip +endif + +# The command used to delete file. +#RM = rm -f + +ETAGS = etags +ETAGSFLAGS = + +CTAGS = ctags +CTAGSFLAGS = + +## Stable Section: usually no need to be changed. +##========================================================================== +SHELL = /bin/sh +EMPTY = +SPACE = $(EMPTY) $(EMPTY) +ifeq ($(PROGRAM),) + CUR_PATH_NAMES = $(subst /,$(SPACE),$(subst $(SPACE),_,$(CURDIR))) + PROGRAM = $(word $(words $(CUR_PATH_NAMES)),$(CUR_PATH_NAMES)) + ifeq ($(PROGRAM),) + PROGRAM = a.out + endif +endif +ifeq ($(SRCDIRS),) + SRCDIRS = . +endif +SOURCES = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS)))) +HEADERS = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(HDREXTS)))) +SRC_CXX = $(filter-out %.c,$(SOURCES)) +OBJS = $(addsuffix .o, $(basename $(SOURCES))) +DEPS = $(OBJS:.o=.d) + +## Define some useful variables. +#DEP_OPT = $(shell if `$(CC) --version | grep "GCC"`; then echo "-MM -MP"; else echo "-M"; fi ) +DEP_OPT = -M +DEPEND = $(CC) $(DEP_OPT) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) +DEPEND.d = $(subst -g ,,$(DEPEND)) +COMPILE.c = $(CC) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) -c +COMPILE.cxx = $(CXX) $(MY_CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c +LINK.c = $(CC) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) +LINK.cxx = $(CXX) $(MY_CFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) + +.PHONY: all objs tags ctags clean distclean help show objclean + +# Delete the default suffixes +.SUFFIXES: + +all: $(PROGRAM) + +# Rules for creating dependency files (.d). +#------------------------------------------ + +%.d:%.c + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +%.d:%.C + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +%.d:%.cc + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +%.d:%.cpp + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +%.d:%.CPP + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +%.d:%.c++ + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +%.d:%.cp + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +%.d:%.cxx + @echo -n $(dir $<) > $@ + @$(DEPEND.d) $< >> $@ + +# Rules for generating object files (.o). +#---------------------------------------- +objs:$(OBJS) + +%.o:%.c + @$(COMPILE.c) $< -o $@ + @echo "[M] $@" + +%.o:%.C + @$(COMPILE.cxx) $< -o $@ + @echo "[M] $@" + +%.o:%.cc + @$(COMPILE.cxx) $< -o $@ + @echo "[M] $@" + +%.o:%.cpp + @$(COMPILE.cxx) $< -o $@ + @echo "[M] $@" + +%.o:%.CPP + $(COMPILE.cxx) $< -o $@ + +%.o:%.c++ + $(COMPILE.cxx) $< -o $@ + +%.o:%.cp + $(COMPILE.cxx) $< -o $@ + +%.o:%.cxx + @$(COMPILE.cxx) $< -o $@ + @echo "[M] $@" + +# Rules for generating the tags. +#------------------------------------- +tags: $(HEADERS) $(SOURCES) + $(ETAGS) $(ETAGSFLAGS) $(HEADERS) $(SOURCES) + +ctags: $(HEADERS) $(SOURCES) + $(CTAGS) $(CTAGSFLAGS) $(HEADERS) $(SOURCES) + +# Rules for generating the executable. +#------------------------------------- +$(PROGRAM):$(OBJS) +ifeq ($(SRC_CXX),) + @$(LINK.c) $(OBJS) $(MY_LIBS) -o $@ + @echo "[L] $@" + @$(STRIP) $(PROGRAM) + @echo "[S] $(PROGRAM)" + @$(RM) $(DEPS) + @echo "*** version:$(VER)-$(PLAT): Build $@ Finished ***" +else + @$(LINK.cxx) $(OBJS) $(MY_LIBS) -o $@ + @echo "[L] $@" + @$(STRIP) $(PROGRAM) + @echo "[S] $(PROGRAM)" + @$(RM) $(DEPS) + @echo "*** version:$(VER)-$(PLAT): Build $@ Finished ***" +endif + +ifndef NODEP +ifneq ($(DEPS),) + sinclude $(DEPS) +endif +endif + +clean: + $(RM) $(OBJS) $(PROGRAM) $(DEPS) + +distclean: clean + $(RM) $(DEPS) TAGS + +objclean: + $(RM) $(OBJ) $(DEPS) + +# Show help. +help: + @echo 'Generic Makefile for C/C++ Programs version' + @echo + @echo 'Usage: make [TARGET]' + @echo 'TARGETS:' + @echo ' all (=make) compile and link.' + @echo ' NODEP=yes make without generating dependencies.' + @echo ' objs compile only (no linking).' + @echo ' tags create tags for Emacs editor.' + @echo ' ctags create ctags for VI editor.' + @echo ' clean clean objects and the executable file.' + @echo ' distclean clean objects, the executable and dependencies.' + @echo ' show show variables (for debug use only).' + @echo ' help print this message.' + @echo + @$(RM) $(DEPS) + +# Show variables (for debug use only.) +show: + @echo 'PROGRAM :' $(PROGRAM) + @echo 'PLAT :' $(PLAT) + @echo 'TYPE :' $(TYPE) + @echo 'SRCDIRS :' $(SRCDIRS) + @echo 'HEADERS :' $(HEADERS) + @echo 'SOURCES :' $(SOURCES) + @echo 'SRC_CXX :' $(SRC_CXX) + @echo 'OBJS :' $(OBJS) + @echo 'DEPS :' $(DEPS) + @echo 'DEPEND :' $(DEPEND) + @echo 'COMPILE.c :' $(COMPILE.c) + @echo 'COMPILE.cxx :' $(COMPILE.cxx) + @echo 'link.c :' $(LINK.c) + @echo 'link.cxx :' $(LINK.cxx) + + +############################# End of the Makefile ################################ diff --git a/src/cJSON.c b/src/cJSON.c new file mode 100644 index 0000000..3cdc8fa --- /dev/null +++ b/src/cJSON.c @@ -0,0 +1,3096 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#define NAN 0.0/0.0 +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + else + { + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..25d8a00 --- /dev/null +++ b/src/common.c @@ -0,0 +1,218 @@ +#define LOG_TAG "mqtt" +#include "common.h" +#include "elog.h" + +DEV_CFG_INFO dev_cfg; + +char *get_time_in_json(void) +{ + static char time_string[32]; + char ms[8]; + struct timeval tv; + struct tm *now; + + gettimeofday(&tv,NULL); + now = localtime(&tv.tv_sec); + memset(time_string,0,sizeof(time_string)); + strftime(time_string, sizeof(time_string),"%Y-%m-%dT%H:%M:%S",now); + snprintf(ms,sizeof(ms),".%03ld",tv.tv_usec/1000); + strncat(time_string,ms,strlen(ms)); + strncat(time_string,"+0800",strlen("+0800")); + return time_string; +} + +char *make_token(char *token,char len) +{ + static uint32_t token_inc=0; + if(token!=NULL) + snprintf(token,len-1,"%08d",token_inc++); + return token; +} + +int split(char *src,const char *separator,char **dest) +{ + /* + src 源字符串的首地址(buf的地址) + separator 指定的分割字符 + dest 接收子字符串的数组 + num 分割后子字符串的个数 + */ + char *pNext; + int count = 0; + if(src==NULL||strlen(src)==0) //如果传入的地址为空或长度为0,直接终止 + return 0; + if(separator==NULL||strlen(separator)==0) //如未指定分割的字符串,直接终止 + return 0; + pNext = (char *)strtok(src,separator); //必须使用(char *)进行强制类型转换(虽然不写有的编译器中不会出现指针错误) + while(pNext != NULL) { + *dest++ = pNext; + ++count; + pNext = (char *)strtok(NULL,separator); //必须使用(char *)进行强制类型转换 + } + return count; +} + +//ret返回长度 +char *read_file(char *file_name,int32_t *ret) +{ + FILE *f=NULL; + char *content; + int32_t file_len; + if(access(file_name,R_OK)==-1) + { + log_e("access file %s failed",file_name); + return NULL; + } + if((f=fopen(file_name,"r"))==NULL) + { + log_e("open file %s failed",file_name); + return NULL; + } + fseek(f,0,SEEK_END); + file_len=ftell(f); + fseek(f,0,SEEK_SET); + content=(char*)malloc(file_len+1); + if(content == NULL) + { + log_e("malloc failed when !"); + fclose(f); + return NULL; + } + memset(content,0,file_len+1); + fread(content,1,file_len,f); + if(ret!=NULL) + *ret=file_len; + fclose(f); + return content; +} + +cJSON *file_to_json(char *file) +{ + char *file_content; + cJSON *root=NULL; + if((file_content=read_file(file,NULL))!=NULL) + { + if((root=cJSON_Parse(file_content))!=NULL) + { + free(file_content); + //if(cJSON_IsInvalid(root)) + return root; + //cJSON_Delete(root); + } + else + { + /* code */ + free(file_content); + } + } + return NULL; +} + +int32_t read_dev_cfg(DEV_CFG_INFO *p) +{ + cJSON *root; + cJSON *devdesc; + cJSON *port; + cJSON *item; + char *json_string; + int32_t json_len; + + if((json_string=read_file(CFG_FILE,&json_len))==NULL) + { + log_e("read cfg file failed"); + return -1; + } + + root = cJSON_Parse(json_string);//解析json缓冲区 + if(!root) + { + log_e("DevList Invalid Json Error before:[%s]",cJSON_GetErrorPtr()); + free(json_string); + return -2; + } + + if((devdesc=cJSON_GetObjectItem(root,"devdesc"))!=NULL) + { + if((item=cJSON_GetObjectItem(devdesc,"port"))!=NULL) + { + strncpy(p->dev_desc.port,item->valuestring,sizeof(p->dev_desc.port)); + log_i("port=%s",p->dev_desc.port); + } + if((item=cJSON_GetObjectItem(devdesc,"addr"))!=NULL) + { + strncpy(p->dev_desc.addr,item->valuestring,sizeof(p->dev_desc.addr)); + log_i("addr=%s",p->dev_desc.addr); + } + if((item=cJSON_GetObjectItem(devdesc,"desc"))!=NULL) + { + strncpy(p->dev_desc.desc,item->valuestring,sizeof(p->dev_desc.desc)); + log_i("desc=%s",p->dev_desc.desc); + } + if((item=cJSON_GetObjectItem(devdesc,"model"))!=NULL) + { + strncpy(p->dev_desc.model,item->valuestring,sizeof(p->dev_desc.model)); + log_i("model=%s",p->dev_desc.model); + } + } + + if((port=cJSON_GetObjectItem(root,"port"))!=NULL) + { + if((item=cJSON_GetObjectItem(port,"baud"))!=NULL) + { + strncpy(p->port_cfg.baud,item->valuestring,sizeof(p->port_cfg.baud)); + log_i("baud=%s",p->port_cfg.baud); + } + if((item=cJSON_GetObjectItem(port,"bit"))!=NULL) + { + strncpy(p->port_cfg.bits,item->valuestring,sizeof(p->port_cfg.bits)); + log_i("bit=%s",p->port_cfg.bits); + } + if((item=cJSON_GetObjectItem(port,"parity"))!=NULL) + { + strncpy(p->port_cfg.parity,item->valuestring,sizeof(p->port_cfg.parity)); + log_i("parity=%s",p->port_cfg.parity); + } + } + free(json_string); + cJSON_free(root); + return 0; +} + + +int32_t save_to_file(char *file,uint32_t pos,uint8_t *buf,uint32_t size) +{ + FILE *f=NULL; + int ret; + if((f=fopen(file,"w"))==NULL) + { + log_e("open file %s failed",file); + return -1; + } + /*if(pos) + { + fseek(f,pos,SEEK_SET); + }*/ + ret=fwrite(buf,1,size,f); + fclose(f); + return ret; +} + + +int32_t load_from_file(char *file,uint32_t pos,uint8_t *buf,uint32_t size) +{ + FILE *f=NULL; + int ret; + if((f=fopen(file,"r"))==NULL) + { + log_e("open file %s failed",file); + return -1; + } + if(pos) + { + fseek(f,pos,SEEK_SET); + } + ret=fread(buf,1,size,f); + fclose(f); + return ret; +} + diff --git a/src/crc.c b/src/crc.c new file mode 100644 index 0000000..0de6101 --- /dev/null +++ b/src/crc.c @@ -0,0 +1,93 @@ +#include "crc.h" +static unsigned char const auchCRCHi[] = { +0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, +0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, +0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, +0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, +0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, +0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, +0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, +0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, +0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, +0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, +0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, +0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, +0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, +0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, +0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, +0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, +0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, +0x40 +} ; + +static unsigned char const auchCRCLo[] = { +0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, +0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, +0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, +0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, +0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, +0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, +0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, +0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, +0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, +0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, +0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, +0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, +0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, +0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, +0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, +0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, +0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, +0x40 +}; + + + +/********************************************************************** + Function: CRC16 + Description: �������ݵ�CRCֵ + Input: uint8_t *puchMsg //Ҫ���������ݵ�ַ + uint32_t usDataLen //Ҫ�������ݳ��� + Output: + Calls: + Called by: + Others: + History: + 1. Date: 2011/01/17 + Author: TMB + Modification: + **********************************************************************/ +uint16_t crc16(unsigned char *puchMsg,unsigned int usDataLen) +{ +unsigned char uchCRCHi = 0xFF ; +unsigned char uchCRCLo = 0xFF ; +unsigned int uIndex; + +while(usDataLen--) + { + uIndex = uchCRCHi^(*puchMsg); + uchCRCHi = uchCRCLo^auchCRCHi[uIndex]; + uchCRCLo = auchCRCLo[uIndex]; + puchMsg++; + } + return ((unsigned short)uchCRCLo<<8|uchCRCHi); +} + + + +void crc16_ex(unsigned char *puchMsg,unsigned int usDataLen,unsigned char *result) +{ +unsigned char uchCRCHi = 0xFF ; +unsigned char uchCRCLo = 0xFF ; +unsigned int uIndex; + +while(usDataLen--) + { + uIndex = uchCRCHi^(*puchMsg); + uchCRCHi = uchCRCLo^auchCRCHi[uIndex]; + uchCRCLo = auchCRCLo[uIndex]; + puchMsg++; + } + *result= uchCRCHi; + *(result+1)=uchCRCLo; +} \ No newline at end of file diff --git a/src/iec61850_process.c b/src/iec61850_process.c new file mode 100644 index 0000000..2834294 --- /dev/null +++ b/src/iec61850_process.c @@ -0,0 +1,98 @@ +#define LOG_TAG "iec61850" +#include "elog.h" +#include "common.h" +#include +#include "thread.h" +#include +#include "msq.h" +#include "scl_shm.h" +#include "iec61850_process.h" + +mqd_t iec61850_rx_mq;// +SCL_MSG_TYPE recv_iec_msg; +static char iec61850_rx_buf[1024]; + + +int iec61850_send_msg(void *msg,int msg_len) +{ + static mqd_t iec61850_rx_slave_mq=(mqd_t)-1; + if(iec61850_rx_slave_mq==-1) + { + if((iec61850_rx_slave_mq= open_mq(IEC61850_RX_MQ))==((mqd_t)-1)) + return -1; + } + if(mq_send(iec61850_rx_slave_mq,(const char *)msg,msg_len,IEC61850_RX_MQ_PRIO)==-1) + return -2; + + return 0; +} + + +int send_ysp_msg(void *svbr_pri,uint32_t len) +{ + static unsigned char sjzd_msg_buf[512]; + unsigned int cmd; + cmd=CMD_SEND_PRI_DATA; + memcpy(sjzd_msg_buf,&cmd,sizeof(cmd)); + memcpy(&sjzd_msg_buf[4],(const void *)svbr_pri,len); + return iec61850_send_msg(sjzd_msg_buf,len+4); +} + + +void *iec61850_rx_routine(void *arg) +{ + int i; + time_t now; + LN_SVBR_TYPE ln_svbr; + + start_scl_mem(); + if((iec61850_rx_mq=create_mq(IEC61850_RX_MQ,IEC61850_RX_MAX_MESSAGE,IEC61850_RX_MESSAGE_SIZE))<0) + { + log_e("create iec61850 rx message queue failed\n"); + exit(1); + } + + while(1) + { + if(recv_mq_wait(iec61850_rx_mq,(void *)iec61850_rx_buf,sizeof(iec61850_rx_buf),1000000)>0) + { + memcpy(&recv_iec_msg,iec61850_rx_buf,sizeof(recv_iec_msg)); + switch(recv_iec_msg.cmd) + { + case CMD_SEND_PRI_DATA: + log_d("receive CMD_SEND_PRI_DATA\n"); + now=time(NULL); + //lock_input_data(); + //unlock_input_data(); + lock_scl_mem(); + put_mv_data(0,0,(USR_MV *)&ln_svbr,sizeof(ln_svbr)/sizeof(USR_MV)); + unlock_scl_mem(); + break; + default: + break; + } + } + else + { + now=time(NULL); + //lock_input_data(); + //unlock_input_data(); + lock_scl_mem(); + put_mv_data(0,0,(USR_MV *)&ln_svbr,sizeof(ln_svbr)/sizeof(USR_MV)); + unlock_scl_mem(); + } + } + stop_scl_mem(); +} + + +int iec61850_rx_init(void) +{ + pthread_t iec61850_rx_thread; + if((iec61850_rx_thread=task_create(iec61850_rx_routine,NULL,0,0))==-1) + { + log_e("create iec61850_rx_routine failed\n"); + return 1; + } + return 0; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7a3c188 --- /dev/null +++ b/src/main.c @@ -0,0 +1,171 @@ +#define LOG_TAG "main" +#include "main.h" +#include "elog.h" +#include "modbus_rtu_slave.h" +#include "sjzd.h" +#include +#include "iec61850_process.h" + +#define APP_VERSION "SV01.002" +#define START_SMP_HOUR 8 //8点开始采样 +#define SMP_INV 60 //60分钟采样一次 + +#define SJZD_PORT "/dev/ttySZ3" +#define SJZD_BAUD "38400" +#define SJZD_PARITY "no" + +typedef struct +{ + uint32_t has_new_data; + uint32_t new_data_len; + uint32_t data_timestamp; + uint32_t rsv; +} SLAVE_REG_TYPE; + + +static int doit=0; + +void ctrlCfun(int i) +{ + doit = 1; +} + +int log_init(void) +{ + /* close printf buffer */ + setbuf(stdout,NULL); + /* initialize EasyLogger */ + elog_init(); + /* set EasyLogger log format */ + elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);//设置assert级别的输出内容格式 + elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);//错误日志输出消息级别,标签,时间 + elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);//警告日志输出格式设置 + elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME); + elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~ELOG_FMT_FUNC); +#ifdef ELOG_COLOR_ENABLE + elog_set_text_color_enabled(true); +#endif + /* start EasyLogger */ + elog_start(); + return 0; +} + +char *make_file_name(uint32_t sn,uint8_t addr) +{ + static char data_file_name[64]; + struct tm *p; + memset(data_file_name,0,sizeof(data_file_name)); + p=localtime((time_t *)&sn); + sprintf(data_file_name,"%04d%02d%02d_%02d%02d%02d_%d",p->tm_year+1900,p->tm_mon+1,p->tm_mday,p->tm_hour,p->tm_min,p->tm_sec,addr); + return data_file_name; +} + +//1.串口轮询线程, +//2.通讯协议AA+55+addr(2byte)+data_len(2bytes)+data(n)+crc_val+## +int main(int argc,char **args) +{ + int32_t ret; + SLAVE_REG_TYPE slave_regs[3]; + uint32_t tmp_reg; + uint8_t rlt,slave_addr; + int32_t i; + time_t now; + struct tm *p; + uint32_t last_smp_time=0; + char *curr_file; + + signal(SIGINT,ctrlCfun); + + log_init(); + + elog_raw("Application Version:%s\r\n",APP_VERSION); + + //if(read_dev_cfg(&dev_cfg)!=0) + //{ + // log_e("load device config failed"); + //} + + if(sjzd_master_init(SJZD_PORT,SJZD_BAUD,SJZD_PARITY)!=0) + { + log_e("sjzd master init failed"); + return -1; + } + + now=time(NULL); + p=localtime((time_t *)&now); + if(p->tm_hour0) + { + log_d("read data from slave %d ok",slave_addr); + //分析数据文件curr_file + } + else + { + log_e("read data from slave %d failed",slave_addr); + } + + //清数据标志 + tmp_reg=0; + if(sjzd_reg_wr(slave_addr,0,(uint8_t *)&tmp_reg,sizeof(tmp_reg),&rlt)!=FRM_ERR_NONE) + { + log_e("clear slave %d data failed",slave_addr); + } + } + } + } + + //判断是否需要启动采样 + if(p->tm_hour>=START_SMP_HOUR) + { + if(now>=(last_smp_time+SMP_INV*60)) + { + for(i=0;i<3;i++) + { + slave_addr=i+1; + //发送启动采样命令 + tmp_reg=1; + if(sjzd_reg_wr(slave_addr,16,(uint8_t *)&tmp_reg,1,&rlt)!=FRM_ERR_NONE) + { + if(sjzd_reg_wr(slave_addr,16,(uint8_t *)&tmp_reg,1,&rlt)!=FRM_ERR_NONE) + log_e("clear slave %d data failed",slave_addr); + } + else + { + log_d("start slave %d sample succeed",slave_addr); + } + } + last_smp_time=now; + } + } + + }//end while(!doit) + return 0; +} \ No newline at end of file diff --git a/src/modbus_rtu_slave.c b/src/modbus_rtu_slave.c new file mode 100644 index 0000000..e4f1e3b --- /dev/null +++ b/src/modbus_rtu_slave.c @@ -0,0 +1,263 @@ +#define LOG_TAG "modbus_rtu" +#include "elog.h" +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include +#include "modbus_rtu_slave.h" + +#define REG_ADDR (header_length+1) +#define REG_NUM (header_length+3) +#define REG_VAL (header_length+3) + +enum { + TCP, + TCP_PI, + RTU +}; + +static pthread_mutex_t modbus_rtu_slave_mutex; +static modbus_mapping_t *mb_mapping = NULL; +//YSP_MODBUS_REGS ysp_modbus_data; +static uint32_t coil_arr[4];//128个线圈 + +static void modbus_rtu_slave_lock(void) +{ + pthread_mutex_lock(&modbus_rtu_slave_mutex); +} + +static void modbus_rtu_slave_unlock(void) +{ + pthread_mutex_unlock(&modbus_rtu_slave_mutex); +} + +void modbus_rtu_data_fresh(void *data,int is_new) +{ + static int data_index=0; + modbus_rtu_slave_lock(); + + modbus_rtu_slave_unlock(); +} + +//设置modbus线圈值 +void modbus_rtu_set_coil() +{ + modbus_rtu_slave_lock(); + + modbus_rtu_slave_unlock(); +} + + +//从modbus获取线圈值 +void modbus_rtu_get_coil() +{ + modbus_rtu_slave_lock(); + + modbus_rtu_slave_unlock(); +} + +void modbus_rtu_slave_routine(void *arg) +{ + int s = -1; + int i = 0; + int rc = 0; + modbus_t *ctx = NULL; + uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];//接收缓冲区 + int header_length; + uint16_t *ptr; + int use_backend=RTU; + //int use_backend=TCP; + uint16_t reg_addr,reg_val,reg_num; + + if(use_backend == TCP) { + ctx = modbus_new_tcp("127.0.0.1",1502); + } + else if (use_backend == TCP_PI) { + ctx = modbus_new_tcp_pi("::0","1502"); + } + else + { + //打开端口: 端口,波特率,校验位,数据位,停止位 + ctx = modbus_new_rtu(MODBUS_RTU_SERIAL,MODBUS_RTU_BAUD,'N',8,1); + } + + //设置从机地址 + modbus_set_slave(ctx,SERVER_ID); + + header_length = modbus_get_header_length(ctx); + + modbus_set_debug(ctx,TRUE); + +/*mb_mapping = modbus_mapping_new_start_address(UT_BITS_ADDRESS,UT_BITS_NB,UT_INPUT_BITS_ADDRESS,UT_INPUT_BITS_NB,UT_REGISTERS_ADDRESS,UT_REGISTERS_NB_MAX,UT_INPUT_REGISTERS_ADDRESS,UT_INPUT_REGISTERS_NB);*/ + //油色谱总共24个寄存器 + mb_mapping = modbus_mapping_new(128,0,MODBUS_MAX_READ_REGISTERS,0); + if(mb_mapping == NULL) { + log_e("Failed to allocate the mapping: %s\n",modbus_strerror(errno)); + modbus_free(ctx); + return ; + } + + //初始化寄存器 + modbus_rtu_slave_lock(); + + modbus_rtu_slave_unlock(); + + // Initialize input values that's can be only done server side. + //modbus_set_bits_from_bytes(mb_mapping->tab_input_bits,0,UT_INPUT_BITS_NB,UT_INPUT_BITS_TAB); + + // Initialize values of INPUT REGISTERS + //for(i=0; i < UT_INPUT_REGISTERS_NB; i++) { + // mb_mapping->tab_input_registers[i] = UT_INPUT_REGISTERS_TAB[i]; + //} + + if(use_backend == TCP) + { + s = modbus_tcp_listen(ctx,1); + modbus_tcp_accept(ctx,&s); + } + else if(use_backend == TCP_PI) + { + s = modbus_tcp_pi_listen(ctx,1); + modbus_tcp_pi_accept(ctx,&s); + } + else + { + //建立连接 + rc = modbus_connect(ctx); + if (rc == -1) + { + log_e("unable to connect %s\n",modbus_strerror(errno)); + modbus_free(ctx); + return ; + } + } + + while (1) + { + do{ + memset(query,0,sizeof(query)); + rc = modbus_receive(ctx,query); + } while (rc == 0); + + if(rc ==-1&&errno!=EMBBADCRC) { + log_e("parse modbus frame error: %s\n",modbus_strerror(errno)); + //break; + continue; + } + + switch(query[header_length]) + { + case MODBUS_FC_READ_COILS://Read Coils in mb_mapping->tab_bits:IO输出 + //读继电器 + /*coil_arr[0]=relay_get_all(NULL); + memcpy(mb_mapping->tab_bits,coil_arr,4);*/ + break; + case MODBUS_FC_READ_DISCRETE_INPUTS://Read Discrete Inputs in mb_mapping->tab_input_bits:IO输入 + log_e("unsurported function 2\n"); + //否定应答 + modbus_reply_exception(ctx,query,MODBUS_EXCEPTION_ILLEGAL_FUNCTION); + continue; + break; + case MODBUS_FC_READ_HOLDING_REGISTERS://Read Holding Registers in mb_mapping->tab_registers:REG输出 + //读寄存器 + /*if((MODBUS_GET_INT16_FROM_INT8(query,REG_ADDR)+MODBUS_GET_INT16_FROM_INT8(query,REG_NUM))>(sizeof(YSP_MODBUS_REGS)/2)) + { + log_e("reply to this special register address by an exception\n"); + //否定应答 + modbus_reply_exception(ctx,query,MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS); + continue; + }*/ + //更新寄存器 + modbus_rtu_slave_lock(); + /*ptr=(uint16_t *)&ysp_modbus_data; + for(i=0;i<(sizeof(YSP_MODBUS_REGS)/2);i++) + { + mb_mapping->tab_registers[i] = ptr[i]; + }*/ + modbus_rtu_slave_unlock(); + break; + case MODBUS_FC_READ_INPUT_REGISTERS://Read Input Registers in mb_mapping->tab_input_registers:REG输入 + //可用于读取测量值等不可上位机修改的量 + break; + case MODBUS_FC_WRITE_SINGLE_COIL://Write Single Coil:slave_addr+cmd+reg_addr+[FF 00 or 00 00]+crc corresponding to mb_mapping->tab_bits + //写单个继电器 + reg_addr=MODBUS_GET_INT16_FROM_INT8(query,REG_ADDR); + reg_val=MODBUS_GET_INT16_FROM_INT8(query,REG_VAL); + /*if(reg_addr<32) + { + relay_set(reg_addr,(reg_val==0xFF00?1:0),1); + } + else + { + switch(reg_addr) + { + case 32: + break; + case 33: + break; + default: + break; + } + }*/ + break; + case MODBUS_FC_WRITE_SINGLE_REGISTER://Write Single Register:slave_addr+cmd+reg_addr+reg_val+crc corresponding to mb_mapping->tab_registers + //写单个寄存器 + reg_addr=MODBUS_GET_INT16_FROM_INT8(query,REG_ADDR); + reg_val=MODBUS_GET_INT16_FROM_INT8(query,REG_VAL); + break; + case MODBUS_FC_WRITE_MULTIPLE_COILS://Write Multiple Coils:slave_addr+cmd+reg_addr+coil_num+bit_arr+crc corresponding to mb_mapping->tab_bits + //写多个继电器 + break; + case MODBUS_FC_WRITE_MULTIPLE_REGISTERS://Write Multiple Registers:slave_addr+cmd+reg_addr+reg_num+reg_val_arr+crc corresponding to mb_mapping->tab_registers + //写多个寄存器 + reg_addr=MODBUS_GET_INT16_FROM_INT8(query,REG_ADDR); + reg_num=MODBUS_GET_INT16_FROM_INT8(query,REG_NUM); + break; + default:// + break; + } + + //正常应答 + rc = modbus_reply(ctx,query,rc,mb_mapping); + if(rc==-1){ + log_e("reply modbus frame error: %s\n",modbus_strerror(errno)); + //break; + } + } + + if(use_backend==TCP) + { + if(s != -1) + { + close(s); + } + } + + log_e("quit the loop: %s\n",modbus_strerror(errno)); + modbus_mapping_free(mb_mapping); + // For RTU + modbus_close(ctx); + modbus_free(ctx); +} + + +int modbus_rtu_slave_init(void) +{ + pthread_t modbus_rtu_slave_thread; + if(pthread_mutex_init(&modbus_rtu_slave_mutex,NULL)!=0) + { + log_e("init modbus_rtu_slave_mutex failed\r\n"); + return -1; + } + if((modbus_rtu_slave_thread=task_create(modbus_rtu_slave_routine,NULL,0,0))==-1) + { + log_e("create modbus_rtu_slave_routine failed\n"); + return -2; + } + return 0; +} diff --git a/src/msq.c b/src/msq.c new file mode 100644 index 0000000..bb6d964 --- /dev/null +++ b/src/msq.c @@ -0,0 +1,64 @@ +#include "msq.h" +#include +/* +#define S_IRWXU 00700 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 + +#define S_IRWXG 00070 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 + +#define S_IRWXO 00007 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 +#define MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP +*/ + +#define MODE S_IRWXU|S_IRWXG|S_IRWXO +mqd_t create_mq(const char *mq_name,long int mq_maxmsg,long int mq_msgsize) +{ + mqd_t my_mq=(mqd_t)(-1); + struct mq_attr my_attr; + + my_attr.mq_flags=0; + my_attr.mq_maxmsg=mq_maxmsg; + my_attr.mq_msgsize=mq_msgsize; + my_mq=mq_open(mq_name,O_CREAT|O_RDONLY/*|O_NONBLOCK*/,MODE,&my_attr); + return my_mq; +} + + +mqd_t open_mq(const char *mq_name) +{ + return mq_open(mq_name,O_WRONLY|O_NONBLOCK,0,NULL); +} + + +//timeouts is in us +ssize_t recv_mq_wait(mqd_t mq,void *msg,int size,int timeouts) +{ + struct timespec timeout; + struct timeval now; + ssize_t length; + + gettimeofday(&now,NULL); + now.tv_usec+=timeouts; + if(now.tv_usec>=1000000) + { + now.tv_sec+=(now.tv_usec/1000000); + now.tv_usec=now.tv_usec%1000000; + } + timeout.tv_sec=now.tv_sec; + timeout.tv_nsec= now.tv_usec*1000; + length=mq_timedreceive(mq,(char*)msg,size,NULL,&timeout); + return length; +} + + + + + diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..0d02021 --- /dev/null +++ b/src/serial.c @@ -0,0 +1,229 @@ +#define LOG_TAG "serial" +#include "serial.h" +#include "elog.h" +/* +TTU使用4路UART实现两路RS485串口、两路RS232/RS485可切换串口,设备节点与串口对应关系为: +UART 设备节点 对应串口 GPIO状态 +3 /dev/ttySZ3 RS485-1 - +4 /dev/ttySZ4 RS485-2 - +5 /dev/ttySZ5 RS485-3 PB08为高电平 +6 /dev/ttySZ6 RS485-4 PB09为高电平 +7 /dev/ttySZ5 RS232-1 PB08为低电平 +8 /dev/ttySZ6 RS232-2 PB09为低电平 +详情参考:边缘计算框架使用说明/融合终端驱动程序使用说明书 +*/ + +static int speed_arr[] = {B115200,B57600,B38400,B19200,B9600,B4800,B2400,B1200,B300}; +static int name_arr[] = {115200,57600,38400,19200,9600,4800,2400,1200,300}; + +// Common Helper ///////////////////////////////////////////////////////////// +// +static void SetACommMode(int fd,int combaud,int databit,int stopbit,int checkmode) +{ + struct termios tty_termios; + int flags; + unsigned int i; + + if ((tcgetattr(fd, &tty_termios) == -1)) { + log_e("tcgetattr()"); + return; + } + cfmakeraw(&tty_termios); + for (i=0; i0) + timeout /= 100; + + +#ifdef DEBUG_COM + int i; + printf("RX:\t"); +#endif + + for(;;) + { + iDataNum = read(m_comFd,buf+rxlen,sz-rxlen); +#ifdef DEBUG_COM + for (int i = 0; i < iDataNum; i++) { + printf("%02X ", buf[rxlen + i]); + if (!((i + 1) % 10)) + printf("\n\t"); + } +#endif + + if (iDataNum <= 0) { + if (recvflag==0 && timeout--) + usleep(100000);//100ms + else + break; + } else { + recvflag = 1; + rxlen += iDataNum; + } + } + + return (rxlen > 0) ? rxlen : 0; +} + +//timeout is in 100ms +int SerialReadEx(int m_comFd,unsigned char *buf,int sz,int timeout) +{ + int curr_length=0,last_length=0,rx_length=0; + //int rx_tick=0; + //tcflush(m_comFd,TCIFLUSH); + while(timeout--) + { + usleep(100000);//wait 100ms + curr_length=read(m_comFd,buf+rx_length,sz-rx_length); + if(curr_length>0) + { + rx_length+=curr_length; + last_length=curr_length; + if(rx_length>=sz) + { + return rx_length; + } + } + else + { + if(last_length>0)//上次读取到了字节本次没有,认为读完了一帧 + { + return rx_length; + } + last_length=0; + //rx_length=0; + } + } + return rx_length; +} + +/** + * Write to Serial device + */ +int SerialWrite(int m_comFd,const unsigned char *buf,int sz) +{ + // Avoid rush + usleep(10000); + tcflush(m_comFd,TCOFLUSH); + if (write(m_comFd, buf, sz) != sz) { + log_e("SerialWrite:write()"); + return -1; + } + +#ifdef DEBUG_COM + int i; + printf("\nTX:\t"); + for (i = 0; i < len; i++) { + printf("%02X ", buf[i]); + if (!((i + 1) % 10)) + printf("\n\t"); + } + printf("\n"); + fflush(stdout); +#endif + return sz; +} diff --git a/src/sjzd.c b/src/sjzd.c new file mode 100644 index 0000000..2f4c4e9 --- /dev/null +++ b/src/sjzd.c @@ -0,0 +1,425 @@ +/* +********************************************************************************************************* +* +* 模块名称:modbus规约模块 +* 文件名称:modbus.c +* 版 本:V1.0 +* 说 明: 本模块主要用于实现modbus规约编解码实现 +* +* 修改记录: +* 版本号 日期 作者 说明 +* V1.0 2020-07-22 谭美兵 正式发布 +* +* Copyright (C), 2020-2030, 上海欣影电力 www.xinyingpower.com +* +********************************************************************************************************* +*/ +#define LOG_TAG "sjzd" +#include "sjzd.h" +#include "elog.h" +//2.通讯协议AA+addr(1byte)+data_len(2bytes)+data(n)+crc_val(2byte)+0x55 +/* +********************************************************************************************************* +* modbus_make_frm() +* +* Description : 构建modbus通讯帧 +* +* Argument(s) : enc_buf modbus报文存储地址 +* slave_addr 从机地址 +* cmd 命令码 +* reg_addr 寄存器起始地址 +* num_or_data 寄存器个数或者待下发的寄存器值 +* Return(s) : 报文长度 +* +* Caller(s) : device_manager. +* +* Note(s) : none. +********************************************************************************************************* +*/ +int32_t sjzd_make_frm(uint8_t *enc_buf,uint8_t slave_addr,uint8_t cmd,uint8_t *data,uint16_t data_len) +{ + int i=0; + uint16_t len=1; + + enc_buf[i++]=0xAA; + enc_buf[i++]=slave_addr;//从机地址 + if(data!=NULL&&data_len!=0) + { + len=data_len+len; + //数据长度 + enc_buf[i++]=len; + enc_buf[i++]=(unsigned char)(len>>8); + //命令 + enc_buf[i++]=cmd; + //拷贝数据 + memcpy(&enc_buf[i],data,data_len); + i+=data_len; + } + else + { + //数据长度 + enc_buf[i++]=len; + enc_buf[i++]=(unsigned char)(len>>8); + //命令 + enc_buf[i++]=cmd; + } + crc16_ex(enc_buf,len+4,&enc_buf[i]);//crc检验 + i+=2; + enc_buf[i++]=0x55; + return i; +} + +int32_t sjzd_chk_frm(uint8_t *buf,uint16_t len,uint8_t slave_addr,uint8_t cmd) +{ + uint8_t crc_val[2]; + uint16_t i; + uint16_t rx_len; + if(buf[0]!=0xAA) + { + memcpy(&buf[0],&buf[1],len-1); + if(buf[0]!=0xAA) + { + return FRM_ERR_START; + } + len-=1; + } + + if(buf[1]!=slave_addr)// + { + return FRM_ERR_SLAVE_ADDR; + } + + rx_len=(uint16_t)buf[3]*256+buf[2]; + + if((rx_len+6)>len) + { + return FRM_ERR_BYTE_COUNT; + } + + if(buf[4]!=cmd)// + { + return FRM_ERR_FC; + } + + crc16_ex(buf,rx_len+4,crc_val);// + if((crc_val[0]==(buf[rx_len+4]))&&(crc_val[1]==(buf[rx_len+5]))) + return FRM_ERR_NONE; + if((crc_val[0]==(buf[rx_len+5]))&&(crc_val[1]==(buf[rx_len+4]))) + return FRM_ERR_NONE; + return FRM_ERR_CRC; +} + +static int32_t sjzd_swp_uint16(uint8_t *buf,uint32_t len) +{ + uint32_t i; + uint8_t tmp; + for(i=0;i从机应答文件长度,文件长度为零表示文件不存在或打开文件失败 + uint8_t sjzd_tx_buf[FRM_MAX_LEN]; + int32_t sjzd_tx_len; + uint8_t sjzd_rx_buf[FRM_MAX_LEN]; + uint16_t sjzd_rx_len=0; + int32_t ret; + + //发送读文件请求 + memset(sjzd_rx_buf,0,sizeof(sjzd_rx_buf)); + strncpy(sjzd_rx_buf,in_file,MAX_PAYLOAD_SIZE-2); + sjzd_tx_len=sjzd_make_frm(sjzd_tx_buf,slave_node,SJZD_RD_FILE_START,(uint8_t *)sjzd_rx_buf,strlen(sjzd_rx_buf)+1); + sjzd_serial_write(sjzd_tx_buf,sjzd_tx_len); + + //读取应答 + sjzd_serial_read(sjzd_rx_buf,4+1+4+2+2,&sjzd_rx_len); + if(sjzd_rx_len<13) + { + //log_e("mb_rx_len=%d,mb_tx_len=%d",mb_rx_len,mb_tx_len); + ret= FRM_ERR_BYTE_COUNT; + goto EXIT; + } + //应该check一下返回值 + if((ret=sjzd_chk_frm(sjzd_rx_buf,sjzd_rx_len,slave_node,SJZD_RD_FILE_START))==FRM_ERR_NONE) + { + //elog_hexdump("modbus_rx",16,mb_rx_buf,mb_rx_len); + if(info!=NULL) + memcpy(info,&sjzd_rx_buf[5],4); + return FRM_ERR_NONE; + } +EXIT: + log_e("%d",ret); + if(sjzd_rx_len) + elog_hexdump("sjzd_read_file_info",16,sjzd_rx_buf,sjzd_rx_len); + return ret; +} + +static int32_t sjzd_read_file_content(uint8_t slave_node,uint32_t ofs,uint8_t *buf,uint32_t len,uint32_t *ret_len) +{ + //2.主机发送读取文件内容[文件地址+长度]<-------->从机应答文件内容[cmd+文件内容] + uint8_t sjzd_tx_buf[FRM_MAX_LEN]; + int32_t sjzd_tx_len; + uint8_t sjzd_rx_buf[FRM_MAX_LEN]; + uint16_t sjzd_rx_len=0; + int32_t ret; + + memcpy(sjzd_rx_buf,&ofs,4); //偏移地址 + memcpy(&sjzd_rx_buf[4],&len,4);//读取长度 + sjzd_tx_len=sjzd_make_frm(sjzd_tx_buf,slave_node,SJZD_RD_FILE_CONTENT,(uint8_t *)sjzd_rx_buf,8); + sjzd_serial_write(sjzd_tx_buf,sjzd_tx_len);//发送命令 + + //读取应答 + sjzd_serial_read(sjzd_rx_buf,8+len,&sjzd_rx_len); + if(sjzd_rx_len<8) + { + //log_e("mb_rx_len=%d,mb_tx_len=%d",mb_rx_len,mb_tx_len); + ret= FRM_ERR_BYTE_COUNT; + goto EXIT; + } + //应该check一下返回值 + if((ret=sjzd_chk_frm(sjzd_rx_buf,sjzd_rx_len,slave_node,SJZD_RD_FILE_CONTENT))==FRM_ERR_NONE) + { + //elog_hexdump("modbus_rx",16,mb_rx_buf,mb_rx_len); + if(ret_len!=NULL) + *ret_len=sjzd_rx_len-8; + return FRM_ERR_NONE; + } +EXIT: + log_e("%d",ret); + if(sjzd_rx_len) + elog_hexdump("sjzd_read_file_content",16,sjzd_rx_buf,sjzd_rx_len); + return ret; +} + +static int32_t sjzd_read_file_end(uint8_t slave_node,char *in_file) +{ + uint8_t sjzd_tx_buf[FRM_MAX_LEN]; + int32_t sjzd_tx_len; + uint8_t sjzd_rx_buf[FRM_MAX_LEN]; + uint16_t sjzd_rx_len=0; + int32_t ret; + int32_t dumy_val=0; + + //3.主机发送读取结束命令<-------->从机原文应答 + sjzd_tx_len=sjzd_make_frm(sjzd_tx_buf,slave_node,SJZD_RD_FILE_END,(uint8_t *)&dumy_val,sizeof(dumy_val)); + sjzd_serial_write(sjzd_tx_buf,sjzd_tx_len);//发送命令 + + //读取应答 + sjzd_serial_read(sjzd_rx_buf,8+sizeof(dumy_val),&sjzd_rx_len); + if(sjzd_rx_len<(8+sizeof(dumy_val))) + { + //log_e("mb_rx_len=%d,mb_tx_len=%d",mb_rx_len,mb_tx_len); + ret= FRM_ERR_BYTE_COUNT; + goto EXIT; + } + //应该check一下返回值 + if((ret=sjzd_chk_frm(sjzd_rx_buf,sjzd_rx_len,slave_node,SJZD_RD_FILE_END))==FRM_ERR_NONE) + { + //elog_hexdump("modbus_rx",16,mb_rx_buf,mb_rx_len); + return FRM_ERR_NONE; + } +EXIT: + log_e("%d",ret); + if(sjzd_rx_len) + elog_hexdump("sjzd_read_file_end",16,sjzd_rx_buf,sjzd_rx_len); + return ret; +} + +//>0表示读取的长度,<0表示出错 +int32_t sjzd_file_rd(uint8_t slave_node,char *in_file,char *out_file) +{ + //1.主机发送待读取文件名<------>从机应答文件长度,文件长度为零表示文件不存在或打开文件失败 + //2.主机发送读取文件内容[文件地址+长度]<-------->从机应答文件内容[cmd+文件内容] + //3.主机发送读取结束命令<-------->从机应答 + int32_t file_tot_len=0; + int32_t i=0; + uint8_t buf[MAX_PAYLOAD_SIZE]; + uint8_t *file_content; + uint32_t curr_len,tmp_len; + int ret=0; + //获取文件长度 + if(sjzd_read_file_info(slave_node,in_file,&file_tot_len)!=FRM_ERR_NONE) + { + log_e("get file %s info failed",in_file); + return -1; + } + log_d("get file %s tot_len=%d bytes",in_file,file_tot_len); + if(file_tot_len<=0) + { + return -2; + } + + //分配缓冲区 + file_content=(uint8_t *)malloc(file_tot_len); + if(file_content==NULL) + { + log_e("alloc file buf failed"); + return -3; + } + + //读取文件内容 + do{ + tmp_len=(MAX_PAYLOAD_SIZE<(file_tot_len-i)?MAX_PAYLOAD_SIZE:(file_tot_len-i)); + log_d("try get file content:ofs=%d len=%d",i,tmp_len); + if(sjzd_read_file_content(slave_node,i,buf,tmp_len,&curr_len)==FRM_ERR_NONE) + { + log_d("get file content:ofs=%d len=%d ok",i,curr_len); + memcpy(&file_content[i],buf,curr_len); + i+=curr_len; + } + else if(sjzd_read_file_content(slave_node,i,buf,tmp_len,&curr_len)==FRM_ERR_NONE) + { + log_d("get file content:ofs=%d len=%d ok",i,curr_len); + memcpy(&file_content[i],buf,curr_len); + i+=curr_len; + } + else + { + log_e("get file content failed:ofs=%d len=%d",i,tmp_len); + ret=-4; + goto EXIT; + } + }while(i=0)//0:无校验 1:奇校验 2:偶校验 + { + return 0; + } + log_e("open %s failed",dev_name); + return -3; +} + +int32_t sjzd_serial_close(void) +{ + if(sjzd_serial_fd!=-1) + { + close(sjzd_serial_fd); + sjzd_serial_fd=-1; + } + return 0; +} + +int32_t sjzd_serial_read(uint8_t *pucBuffer,uint16_t usNBytes,uint16_t *usNBytesRead) +{ + int rx_len; + rx_len=SerialReadEx(sjzd_serial_fd,pucBuffer,usNBytes,MODBUS_TIMEOUT); + if(usNBytesRead!=NULL) + { + if(rx_len>0) + { + *usNBytesRead=rx_len; + } + else + { + /* code */ + *usNBytesRead=0; + } + } + return rx_len; +} + +int32_t sjzd_serial_write(uint8_t *pucBuffer,uint16_t usNBytes) +{ + return SerialWrite(sjzd_serial_fd,pucBuffer,usNBytes); +} \ No newline at end of file diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000..ce8e2db --- /dev/null +++ b/src/thread.c @@ -0,0 +1,42 @@ +/* + * thread.c + * + * Created on: Mar 9, 2012 + * Author: tsx + */ +#define LOG_TAG "thread" +#include "thread.h" +#include "elog.h" + +pthread_t task_create(void *(*start_routine)(void *),void *arg,int prio,int stacksize) +{ + struct sched_param sch; + pthread_attr_t attr; + pthread_t pt; + int ret; + + if(prio!=0&&stacksize!=0) + { + pthread_attr_init(&attr); + //get attr + pthread_attr_getschedparam(&attr,&sch); + //set prio + sch.sched_priority=prio; + pthread_attr_setschedparam(&attr,&sch); + //set stack size + pthread_attr_setstacksize(&attr,stacksize); + //create thread + ret=pthread_create(&pt,&attr,start_routine,arg); + } + else + { + ret=pthread_create(&pt,NULL,start_routine,arg); + } + + if(ret!=0) + { + log_e("create thread failed"); + return -1; + } + return pt; +}