commit f9066d94a953928009c4e2bff777c58b4a7b65c9 Author: wangbo Date: Fri Sep 22 06:52:53 2023 +0000 ADD| new 远程命令行; diff --git a/README b/README new file mode 100755 index 0000000..27d9601 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +操作方法: +cd build +make sshcmd \ No newline at end of file diff --git a/app/include/array.h b/app/include/array.h new file mode 100644 index 0000000..74964d4 --- /dev/null +++ b/app/include/array.h @@ -0,0 +1,75 @@ +/***************************************************************************** + * file include/array.h + * author YuLiang + * version 1.0.0 + * date 22-Sep-2021 + * brief This file provides all the headers of the array functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _ARRAY_H_ +#define _ARRAY_H_ + +/* Includes ------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ +#define ARRAY_MIN_SIZE 1 + +/* Exported types ------------------------------------------------------------*/ +typedef struct +{ + uint32_t active; /* 当前可使用的索引位置. */ + uint32_t alloced; /* 申请的内存大小. */ + void **index; /* 数组元素. */ +} array_t; + +/* Extern global variables ---------------------------------------------------*/ + +/* Exported macro ------------------------------------------------------------*/ + +/* 获取数组A索引I的数据. */ +#define array_get(A, I) ((A)->index[(I)]) +/* 获取数组A目前激活的索引. */ +#define array_active(A) ((A)->active) + +/* Exported functions --------------------------------------------------------*/ +extern array_t *array_init(uint32_t size, int32_t mem_type); +extern void array_set(array_t *a, uint32_t i, void *val, int32_t mem_type); +extern void array_unset(array_t *v, uint32_t i); +extern uint32_t array_append(array_t *a, void *val, int32_t mem_type); +extern void array_free_wrapper(array_t *a, int32_t mem_type); +extern void array_free_index(array_t *a, int32_t mem_type); +extern void array_free(array_t *a, int32_t mem_type); +extern array_t *array_copy(array_t *a, int32_t mem_type); +extern void array_merge(array_t *a, array_t *b, int32_t mem_type); +extern void *array_lookup(array_t *a, uint32_t i); +extern uint32_t array_count(array_t *a); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/include/better_log.h b/app/include/better_log.h new file mode 100644 index 0000000..e5d9c9d --- /dev/null +++ b/app/include/better_log.h @@ -0,0 +1,183 @@ +/****************************************************************************** + * file include/better_log.h + * author YuLiang + * version 1.0.0 + * date 14-Sep-2021 + * brief This file provides all the headers of the log functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _BETTER_LOG_H_ +#define _BETTER_LOG_H_ + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Define --------------------------------------------------------------------*/ +#define LOG_FILE "PowerIoT.db" +#define LOG_STR_LEN 512 +#define LOG_TABLE_NAME "better_log" +#define LOG_MAX_DECORD 5000 +#define LOG_DEFAULT_SHOW_CNT 100 +#define LOG_DB_FIFO "LOG_DB_FIFO" + +#define LOG_STR "Display default most 100 logs\n" +#define LOG_LEVELS "(errors|warnings|notifications|informational|debug)" +#define LOG_LEVEL_DESC \ + "Error messages\n" \ + "Warning messages\n" \ + "Normal but significant messages\n" \ + "Informational messages\n" \ + "Debug messages\n" +/* Exported types ------------------------------------------------------------*/ +typedef enum +{ + LOG_LVL_ERR, + LOG_LVL_WARN, + LOG_LVL_NOTIF, + LOG_LVL_INFO, + LOG_LVL_DBG, + LOG_LVL_MAX +} LOG_LVL_E; + +typedef enum +{ + LOG_MODE_STDOUT, + LOG_MODE_FILE, + LOG_MODE_MONITOR, + LOG_MODE_MAX +} LOG_MODE_E; + +typedef enum +{ + LOG_DEFAULT, + LOG_CLI, + LOG_MEMORY, + LOG_FIFO, + LOG_GPIO, + LOG_PD, + LOG_DAU, + LOG_CSG, + LOG_STORAGE, + LOG_DEBUG, + LOG_MAX +} LOG_MODULE_E; + +typedef enum +{ + LOG_SHOW_CNT, + LOG_SHOW_LVL, + LOG_SHOW_KEYWORD, + LOG_SHOW_MAX +} LOG_SHOW_E; + +typedef struct _log_lvl +{ + int32_t lvl; + char *describe; +} log_lvl_t; + +typedef struct _log_module +{ + int32_t module; + char *describe; +} log_module_t; + +/* log全局数据结构体. */ +typedef struct _log_sys +{ + int32_t enable_lvl[LOG_MODE_MAX]; /* log模块使能等级,位图表示. */ + int32_t default_lvl; /* 默认log模块使能等级,位图表示. */ + sqlite3 *db; /* log数据库指针. */ + char *filename; /* log数据库名字. */ + int32_t timestamp_precision; /* 默认log时间戳精度. */ + pthread_mutex_t log_mutex; + pthread_mutex_t log_out_mutex; + pthread_mutex_t log_db_mutex; /* 数据库读写互斥锁 */ +} log_sys_t; + +typedef struct +{ + int32_t log_fifo_id; + pthread_mutex_t mutex; /* log fifo mutex */ +} _log_fifo_t; + +typedef struct +{ + LOG_SHOW_E type; + int32_t param; + char log_show_str[LOG_STR_LEN]; +} _log_show_t; + +typedef struct +{ + LOG_MODULE_E module; + LOG_LVL_E lvl; + char log_out_str[LOG_STR_LEN]; +} _log_out_t; + +typedef struct +{ + uint32_t type; + void *data; +} _log_msg_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ +extern log_sys_t *log_sys; + +/* Extern functions ----------------------------------------------------------*/ +/* 配置函数属性,让gcc按printf一样检查函数参数,第b个参数为可变参数. */ +#define BTLOG_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) + +extern void log_out(LOG_MODULE_E module, LOG_LVL_E priority, const char *format, ...) + BTLOG_ATTRIBUTE(3, 4); +extern void log_err(LOG_MODULE_E module, const char *format, ...) + BTLOG_ATTRIBUTE(2, 3); +extern void log_warn(LOG_MODULE_E module, const char *format, ...) + BTLOG_ATTRIBUTE(2, 3); +extern void log_info(LOG_MODULE_E module, const char *format, ...) + BTLOG_ATTRIBUTE(2, 3); +extern void log_notice(LOG_MODULE_E module, const char *format, ...) + BTLOG_ATTRIBUTE(2, 3); +extern void log_debug(LOG_MODULE_E module, const char *format, ...) + BTLOG_ATTRIBUTE(2, 3); + +extern int32_t log_open(); +extern void log_set_level(LOG_MODE_E mode, LOG_LVL_E log_level); +extern void log_unset_level(LOG_MODE_E mode, LOG_LVL_E log_level); +extern int32_t log_level_get_by_name(const char *lvl_name); +extern int32_t log_module_get_by_name( const char *module_name ); +extern void log_backtrace(int32_t priority); +extern void log_clean(void); +extern void log_show( int32_t cnt, LOG_LVL_E priority, const char *key_word ); +extern int32_t log_handle_init(void); +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/cJSON.h b/app/include/cJSON.h new file mode 100644 index 0000000..95a9cf6 --- /dev/null +++ b/app/include/cJSON.h @@ -0,0 +1,300 @@ +/* + 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 15 + +#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 address 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); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + +/* 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/app/include/cli.old b/app/include/cli.old new file mode 100644 index 0000000..7e2e6db --- /dev/null +++ b/app/include/cli.old @@ -0,0 +1,139 @@ +/****************************************************************************** + * file include/cli.h + * author LandPower + * version 1.0.0 + * date 10-Sep-2021 + * brief This file provides all the headers of the cli functions. + + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _CLI_H_ +#define _CLI_H_ + +/* Includes ------------------------------------------------------------------*/ +#include "list.h" + +/* Define --------------------------------------------------------------------*/ +#define CLI_NODE_MAME_LEN 32 +#define CLI_USERNAME_LEN 64 +#define CLI_PARAM_LEN 32 + +#define CLI_USERNAME_DEFAULT "ld" +#define CLI_PASSWORD_DEFAULT "1" + +/* Exported types ------------------------------------------------------------*/ +/* The prototype to which callback functions used to process command line + commands must comply. buf is a buffer into which the output from + executing the command can be written, xWriteBufferLen is the length, in bytes of + the buf buffer, and pcCommandString is the entire string as input by + the user (from which parameters can be extracted). */ +typedef int32_t (*CLI_COMMAND_CALLBACK)(const char *cmd_str); + +/* 节点类型. */ +typedef enum +{ + CLI_NODE_COMMON = 0, + CLI_NODE_PASSWORD, + CLI_NODE_CONFIG, + CLI_NODE_COUNT, +} CLI_NODE_E; + +/* The structure that defines command line commands. A command line command + should be defined by declaring a const structure of this type. */ +typedef struct _cli_command_t +{ + const char * const cmd_str; /* The command that causes pxCommandInterpreter to be executed. For example "help". Must be all lower case. */ + const char * const help_str; /* 帮助字符串,解释命令的用途. */ + const CLI_COMMAND_CALLBACK function; /* A pointer to the callback function that will return the output generated by the command. */ + int8_t param_num; /* Commands expect a fixed number of parameters, which may be zero. */ +} cli_command_t; + +/* 命令保存链表结构体. */ +typedef struct _cli_list_item_t +{ + const cli_command_t *cmd; + struct list_head list; +} cli_list_item_t; + +/* 命令行节点结构体. */ +typedef struct _cli_node_t +{ + char name[CLI_NODE_MAME_LEN]; + uint16_t index; + uint16_t upper_node; + struct list_head list; +} cli_node_t; + +/* 命令行节点结构体. */ +typedef struct _cli_user_t +{ + char username[CLI_USERNAME_LEN]; + char password[CLI_USERNAME_LEN]; + struct list_head list; +} cli_user_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ + +/* Extern functions ----------------------------------------------------------*/ +/* Register the command passed in using the pxCommandToRegister parameter. + Registering a command adds the command to the list of commands that are + handled by the command interpreter. Once a command has been registered it + can be executed from the command line. */ +int32_t cli_register_command(const cli_command_t* const cmd, CLI_NODE_E node); + +/* Runs the command interpreter for the command string "pcCommandInput". Any + output generated by running the command will be placed into buf. + xWriteBufferLen must indicate the size, in bytes, of the buffer pointed to + by buf. + + FreeRTOS_CLIProcessCommand should be called repeatedly until it returns pdFALSE. + + pcCmdIntProcessCommand is not reentrant. It must not be called from more + than one task - or at least - by more than one task at a time. */ +int32_t cli_process_command(const char * const cmd_str); + +/* Return a pointer to the xParameterNumber'th word in pcCommandString. */ +const char *cli_parameter_get(const char *cmd_str, int32_t param_index, int32_t *param_len); + +/* 进入命令行节点. */ +int32_t cli_entry_node(CLI_NODE_E node, int16_t index); + +/* 退出命令行当前节点 */ +void cli_quit_node(void); + +/* 从shell终端读取一行命. */ +char *cli_shell_gets(void); + +int32_t cli_init(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/cmd.h b/app/include/cmd.h new file mode 100755 index 0000000..0b01c7f --- /dev/null +++ b/app/include/cmd.h @@ -0,0 +1,341 @@ +/****************************************************************************** + * file include/cmd.h + * author YuLiang + * version 1.0.0 + * date 09-Oct-2021 + * brief This file provides all the headers of the command functions. + + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _CMD_H_ +#define _CMD_H_ + +/* Includes ------------------------------------------------------------------*/ +#include "array.h" +#include "vty.h" + +/* Define --------------------------------------------------------------------*/ +/* 命令行最大参数个数. */ +#define CMD_ARGC_MAX 16 + +/* rang检查数字最大位数. */ +#define DECIMAL_STRLEN_MAX 10 +#define INIT_MATCHARR_SIZE 10 +#define COMPLETE_BUF_SIZE 256 + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" +#define STATE_START 1 +#define STATE_COLON 2 +#define STATE_DOUBLE 3 +#define STATE_ADDR 4 +#define STATE_DOT 5 +#define STATE_SLASH 6 +#define STATE_MASK 7 + +/* 命令行返回值. */ +#define CMD_SUCCESS 0 +#define CMD_WARNING 1 +#define CMD_ERR_NO_MATCH 2 +#define CMD_ERR_AMBIGUOUS 3 +#define CMD_ERR_INCOMPLETE 4 +#define CMD_ERR_EXEED_ARGC_MAX 5 +#define CMD_ERR_NOTHING_TODO 6 +#define CMD_COMPLETE_FULL_MATCH 7 +#define CMD_COMPLETE_MATCH 8 +#define CMD_COMPLETE_LIST_MATCH 9 +#define CMD_SUCCESS_DAEMON 10 + +#define CONF_BACKUP_EXT ".sav" + +#define CL_COPYRIGHT "Copyright 2017-2018 LandPower." + +/* Common descriptions. */ +#define SHOW_STR "Show running system information\n" +#define IP_STR "IP information\n" +#define IPV6_STR "IPv6 information\n" +#define NO_STR "Negate a command or set its defaults\n" +#define REDIST_STR "Redistribute information from another routing protocol\n" +#define CLEAR_STR "Reset functions\n" +#define RIP_STR "RIP information\n" +#define BGP_STR "BGP information\n" +#define OSPF_STR "OSPF information\n" +#define NEIGHBOR_STR "Specify neighbor router\n" +#define DEBUG_STR "Debugging functions (see also 'undebug')\n" +#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" +#define ROUTER_STR "Enable a routing process\n" +#define AS_STR "AS number\n" +#define MBGP_STR "MBGP information\n" +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" +#define OUT_STR "Filter outgoing routing updates\n" +#define IN_STR "Filter incoming routing updates\n" +#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n" +#define OSPF6_NUMBER_STR "Specify by number\n" +#define INTERFACE_STR "Interface infomation\n" +#define IFNAME_STR "Interface name(e.g. ep0)\n" +#define IP6_STR "IPv6 Information\n" +#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" +#define OSPF6_ROUTER_STR "Enable a routing process\n" +#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" +#define SECONDS_STR "<1-65535> Seconds\n" +#define ROUTE_STR "Routing Table\n" +#define PREFIX_LIST_STR "Build a prefix list\n" + +/* Some macroes */ +#define CMD_OPTION(S) ('[' == (S[0])) +#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ('<' == (S[0]))) +#define CMD_VARARG(S) ('.' ==(S[0])) +#define CMD_RANGE(S) ('<' == (S[0])) +#define CMD_IPV4(S) (0 == strcmp((S), "A.B.C.D")) +#define CMD_IPV4_PREFIX(S) (0 == strcmp((S), "A.B.C.D/M")) +#define CMD_IPV6(S) (0 == strcmp((S), "X:X::X:X")) +#define CMD_IPV6_PREFIX(S) (0 == strcmp((S), "X:X::X:X/M")) + +#define DEV_INFO_DEFAULT_IP "192.168.1.110" +#define DEV_INFO_DEFAULT_MASK "255.255.255.0" +#define DEV_INFO_DEFAULT_GW "192.168.1.1" + +#define DEVICE_RUN_PATH "/home/gis" +#define SAVE_STA_CFG_BAK_FILE 1 +#define SAVE_DEV_CFG_BAK_FILE 2 + +/* Exported types ------------------------------------------------------------*/ +enum +{ + CMD_ATTR_DEPRECATED = 1, + CMD_ATTR_HIDDEN, +}; + +/* 匹配结果类型. */ +typedef enum +{ + no_match, + extend_match, + ipv4_prefix_match, + ipv4_match, + ipv6_prefix_match, + ipv6_match, + range_match, + vararg_match, + partly_match, + exact_match +} MATCH_TYPE_E; + +/* 用于定义命令的宏. */ +#define CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + cmd_element_t cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +#define CMD_FUNC_DECL(funcname) \ + static int funcname(cmd_element_t *, vty_t *, int, const char *[]); + +#define CMD_FUNC_TEXT(funcname) \ + static int funcname \ + (cmd_element_t *self __attribute__ ((unused)), \ + vty_t *vty __attribute__ ((unused)), \ + int argc __attribute__ ((unused)), \ + const char *argv[] __attribute__ ((unused))) + +/* CMD for vty command interafce. Little bit hacky ;-). */ +#define CMD(funcname, cmdname, cmdstr, helpstr) \ + CMD_FUNC_DECL(funcname) \ + CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ + CMD_FUNC_TEXT(funcname) + +#define CMD_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + CMD_FUNC_DECL(funcname) \ + CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ + CMD_FUNC_TEXT(funcname) + +#define CMD_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + CMD_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define CMD_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + CMD_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \ + +/* ALIAS macro which define existing command's alias. */ +#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ + CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + +#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) + +#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0) + +#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0) + +/* 命令行模式节点宏 */ +typedef enum +{ + USERNAME_NODE, /* Username node. Default mode of vty interface. */ + PASSWORD_NODE, /* Password node. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + PORT_NODE, /* Port node. */ + COMMON_NODE, /* Common node. Cmd is common to other node. */ + NODES_COUNT +} NODE_TYPE_E; + +/* config节点配置优先级 */ +typedef enum +{ + CONFIG_PRI_CSG = 0, + CONFIG_PRI_DAU, + CONFIG_PRI_PD, + CONFIG_PRI_COUNT +} CONFIG_PRI_E; + +/* Host configuration variable */ +typedef struct +{ + uint32_t id_minor; /* 设备id */ + uint16_t id_major; /* 设备型号代码. */ + uint8_t mac[MAC_ADDR_LEN]; /* System mac. */ + char host_ip[INET_ADDRSTRLEN]; /* host ip string. */ + char host_mask[INET_ADDRSTRLEN];/* host mask string. */ + char host_gw[INET_ADDRSTRLEN]; /* host gw string. */ + uint32_t factory_date; /* 出厂日期. */ + uint32_t deployment_date; /* 部署日期. */ + char dev_type[FILE_NAME_LEN]; /* 设备型号字符串. */ +} device_info_t; + +/* Host configuration variable */ +typedef struct +{ + char name[FILE_NAME_LEN]; /* Host name of this router. */ + int lines; /* System wide terminal lines. */ + char *version; /* 版本号. */ + char *compile; /* 编译时间. */ + char configfile[FILE_NAME_LEN]; /* config file name of this host */ + char bannerfile[FILE_NAME_LEN]; /* Banner configuration file name. */ +} host_t; + +/* Structure of command element. */ +typedef struct _cmd_element +{ + const char *string; /* Command specification by string */ + const char *doc; /* Documentation of this command */ + array_t *strs; /* Pointing out each description array */ + unsigned int str_size; /* Command index count */ + int (*func)(struct _cmd_element*, vty_t*, int, const char *[]); + int daemon; /* Daemon to which this command belong */ + char *config; /* Configuration string */ + array_t *sub_cfg; /* Sub configuration string */ + unsigned char attr; /* Command attributes */ +} cmd_element_t; + +/* Command description structure. */ +typedef struct +{ + char *cmd; /* Command string. */ + char *str; /* Command's description. */ +} desc_t; + +/* 用于命令行模式节点注册配置保存函数 */ +typedef int cmd_save_config_f(vty_t*); + +/* 命令行模式节点结构体 */ +typedef struct +{ + uint32_t node; /* Node index. */ + uint32_t up_node; /* Upper level node index. */ + uint8_t param_num; /* Prompt param num. */ + char *prompt; /* Prompt character at vty interface. */ + cmd_save_config_f *func; /* Node's configuration write function */ + array_t *cmds; /* Array of this node's command list. */ + array_t *configs; /* 配置函数数组. */ +} cmd_node_t; + +typedef struct +{ + uint32_t node; /* Node index. */ + uint32_t param_num; /* Prompt param num. */ + char cmd[504]; /* 结构体正好是 512byte. */ +} vtycmd_head_t; + +typedef struct +{ + vtycmd_head_t buf_read; + vtycmd_head_t buf_write; + int socket; + pthread_mutex_t wr_mutex; /* buf of mutex*/ + pthread_mutex_t rd_mutex; /* buf of mutex*/ +} vtycmd_t; + + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ +extern host_t host; +extern device_info_t device_info; +extern array_t *cmd_nodes; +extern int8_t is_system_init; + +/* Extern functions ----------------------------------------------------------*/ +static inline cmd_node_t *cmd_node_get(NODE_TYPE_E ntype) +{ + return (cmd_node_t *)array_lookup(cmd_nodes, ntype); +} + +extern void cmd_init(void); +extern int32_t cmd_execute(vty_t *vty); +extern const char *cmd_prompt(NODE_TYPE_E ntype); +extern array_t *cmd_strs_create(const char *string); +extern void cmd_strs_free(array_t *a); +extern void cmd_sort_node(void); +extern void cmd_install_node(cmd_node_t *node, cmd_save_config_f *func); +extern array_t *cmd_describe_command (array_t *cmd_line, vty_t *vty, int32_t *status); +extern char **cmd_complete_command(array_t *vline, vty_t *vty, int32_t *status); +extern void cmd_install_element(NODE_TYPE_E ntype, cmd_element_t *cmd); +extern int32_t cmd_config_node_config_register(int32_t pri, cmd_save_config_f *func); +extern void vtysh_init(void); +extern char *vtysh_prompt (void); +extern int vtysh_rl_question(void); +extern int vtysh_rl_completion(void); +extern char *vtysh_completion_entry_function(const char *ignore, int invoking_key); +extern void vtysh_config_recovery(void); +extern void vtysh_device_save(void); +extern void vtysh_eth0_save(void); +extern int32_t vtysh_config_save(void); +extern int32_t vtysh_host_addr_set(char *addr, char *mask); +extern int32_t vtysh_gateway_set(char *gateway); +extern void vtysh_shell_init(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/common.h b/app/include/common.h new file mode 100755 index 0000000..22d1264 --- /dev/null +++ b/app/include/common.h @@ -0,0 +1,167 @@ +/****************************************************************************** + * file include/common.h + * author YuLiang + * version 1.0.0 + * date 10-Sep-2021 + * brief This file provides all the headers of the common functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include + +#include "array.h" +#include "better_log.h" +#include "dbg.h" +#include "memory.h" +#include "thread_monitor.h" + +/* Define --------------------------------------------------------------------*/ +#define FALSE 0 +#define TRUE 1 + +#define E_NONE 0 +#define E_ERROR -1 +#define E_BAD_PARAM -2 +#define E_MEM -3 +#define E_SYS_CALL -4 +#define E_NULL -5 +#define E_NOT_FOUND -6 +#define E_NOT_IDENTIFY -7 +#define E_TIMEOUT -8 +#define E_SAME -9 + +#define OUT + +#define BACKTRACE_SIZE 20 +#define TIME_STR_LEN 64 +#define RECV_MSG_LEN 1536 +#define DEVICE_INVALID 255 +#define FILE_NAME_LEN 128 +#define DIR_PATH_LEN 512 +#define INET_ADDRSTRLEN 16 +#define INET6_ADDRSTRLEN 46 + +#define ETH_GIS_DEV_ID_LEN 16 +#define ETH_GIS_CMD_PARAM_LEN 16 +#define DEVICE_ID_LEN 6 +#define MAC_ADDR_LEN 6 +#define IP_ADDR_LEN 4 +#define DEV_DATA_LEN 1000 +#define DEV_DATA_BITMAP_LEN 8 +#define DEV_NAME_STR_LEN 32 + +/* Does the I/O error indicate that the operation should be retried later? */ +#define ERRNO_IO_RETRY(EN) \ + (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) + +/* Exported types ------------------------------------------------------------*/ +typedef int8_t bool; + +/* Exported macro ------------------------------------------------------------*/ +#define BITMAP_SET(t, b) ((t) |= 1 << (b)) +#define BITMAP_RESET(t, b) ((t) &= ~(1 << (b))) +#define IS_BITMAP_SET(t, b) ((t) & (1 << b)) + +typedef enum _BOOT_MSG +{ + BOOT_NONE = 0, + BOOT_LOCAL_SERVER_IP_CHANGE, + BOOT_LOCAL_SERVER_PORT_CHANGE, + BOOT_LOCAL_IP_CHANGE, + BOOT_LOCAL_HOST_NAME_CHANGE, + BOOT_LOCAL_RESET, + BOOT_LOCAL_ARM_UPGRADE, + BOOT_LOCAL_FPGA_UPGRADE, + BOOT_REMOTE_SERVER_IP_CHANGE, + BOOT_REMOTE_SERVER_PORT_CHANGE, + BOOT_REMOTE_IP_CHANGE, + BOOT_REMOTE_HOST_NAME_CHANGE, + BOOT_REMOTE_RESET, + BOOT_SYSTEM_RESET, + BOOT_MAX, +} BOOT_MSG; + +#define LD_E_RETURN(_module_, _f_) \ + do { \ + int _rv_ = E_ERROR; \ + if ((_rv_ = _f_) != E_NONE) \ + { \ + DBG(_module_, "ERROR return %d!\r\n", _rv_); \ + return _rv_; \ + } \ + } while(0) + +#define LD_NULL_RETURN(_module_, _f_) \ + do { \ + if (NULL == (_f_)) \ + { \ + DBG(_module_, "ERROR return %d!\r\n", _rv_); \ + return E_NULL; \ + } \ + } while(0) + +/* Extern global variables ---------------------------------------------------*/ + +/* Extern functions ----------------------------------------------------------*/ +extern char *str_to_lower(char *str); +extern char *str_omit_space(char *str, bool omit_end); +extern size_t time_string(int32_t timestamp_precision, char *buf, size_t buflen); +extern const char *safe_strerror(int errnum); +extern void buf_print(char *buf, int32_t len); +extern int32_t mac_generate_from_ip(char *ip_str, uint8_t *mac); +extern uint16_t crc16(uint8_t *data, uint16_t size); +extern uint16_t crc16_modbus(uint8_t *data, uint16_t size); +extern void speed_detection_stat(void); +extern void speed_detection_end(void); +extern int printh(const char *format, ...); +extern char* version_get(); +extern char* version_date_get(); +extern uint16_t sofrware_version_get(void); +extern int32_t str_to_mac(char *mac_str, OUT uint8_t *mac); +extern int32_t str_to_id(char *id_str, OUT uint32_t *id); +extern int32_t bitmap_set(uint8_t *buf, int32_t nbit, int32_t buf_len); +extern int32_t bitmap_reset(uint8_t *buf, int32_t nbit, int32_t buf_len); +extern int32_t is_bitmap_set(uint8_t *buf, int32_t nbit, int32_t buf_len); +extern int32_t time_str_to_long(char *date, char *time, uint32_t *t); +extern uint16_t version_str_to_int(void); +extern void time_set(time_t timestamp); +extern void reboot_system(int module, BOOT_MSG type); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/dbg.h b/app/include/dbg.h new file mode 100755 index 0000000..58555ba --- /dev/null +++ b/app/include/dbg.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * file include/common.h + * author YuLiang + * version 1.0.0 + * date 10-Sep-2021 + * brief This file provides all the headers of the debug functions. + + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _DBG_H_ +#define _DBG_H_ + +/* Includes ------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ +#define DBG_HELP_STR_MAX 32 +#define DBG_INVALID_MODULE 0xffffffff + +/* Exported types ------------------------------------------------------------*/ +/* debug模块. */ +typedef enum +{ + DBG_M_DBG = 0, + DBG_M_CLI, + DBG_M_MTIMER, + DBG_M_PROCESS, + DBG_M_GPIO, + DBG_M_PD, + DBG_M_PD_ERR, + DBG_M_PD_DAU, + DBG_M_PD_DAU_SEND, + DBG_M_PD_DAU_RECV, + DBG_M_PD_DAU_ERR, + DBG_M_FIFO, + DBG_M_FIFO_ERR, + DBG_M_PD_CSG, + DBG_M_PD_CSG_ERR, + DBG_M_STORAGE_ERR, + DBG_M_DEBUG, + DBG_M_PD_CPLD, + DBG_M_COUNT +} DBG_MODULE_E; + +/* debug命令字. */ +typedef enum +{ + DBG_CMD_ON = 0, + DBG_CMD_OFF, + DBG_CMD_ALL_OFF +} DBG_CMD_E; + +/* debug结构体,每个结构体代表一个模块. */ +typedef struct +{ + int32_t num; + int32_t stat; + char desc[DBG_HELP_STR_MAX]; +} dbg_module_t; + +/* Exported macro ------------------------------------------------------------*/ +#ifdef CFG_DBG_ON + #define DBG(_MODULE_, _format_, _msg_...) \ + do { \ + if (dbg_stat_get(_MODULE_)) printh("%s(%d): " _format_, __FUNCTION__, __LINE__, ##_msg_); \ + } while(0) + #define DBG_Q(_MODULE_, _format_, _msg_...) \ + do { \ + if (_dbg_module[_MODULE_].stat) printh(_format_, ##_msg_); \ + } while(0) +#else + #define DBG(_MODULE_, _msg_...) + //#define DBG(_MODULE_, _format_, _msg_...) +#endif + +/* Extern global variables ---------------------------------------------------*/ +#ifdef CFG_DBG_ON +extern dbg_module_t _dbg_module[DBG_M_COUNT]; +#endif + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t dbg_stat_get(DBG_MODULE_E module); +extern int dbg_cmd_hander(DBG_CMD_E cmd, int32_t module); +extern void dbg_init(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/fifo.h b/app/include/fifo.h new file mode 100644 index 0000000..5173569 --- /dev/null +++ b/app/include/fifo.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * file include/fifo.h + * author YuLiang + * version 1.0.0 + * date 21-Feb-2023 + * brief This file provides all the headers of the fifo functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _FIFO_H_ +#define _FIFO_H_ + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Define --------------------------------------------------------------------*/ +#define FIFO_NAME_LEN 32 + +/* Exported types ------------------------------------------------------------*/ + +/* . */ +typedef struct _fifo_t +{ + char name[FIFO_NAME_LEN]; // fifo 名字. + uint32_t size; // fifo 大小. + uint32_t cur; // 当前的数据位置. + uint32_t valid; // 当前读取的数据位置. + uint32_t used; // fifo 使用数量. + uint32_t max; // fifo 最大使用数量. + pthread_mutex_t mutex; // 多线程同时操作的信号量. + sem_t sem; // 读取有效互斥锁. + void **data; // 数据数组. +} fifo_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t fifo_init(void); +extern int32_t fifo_create(char* name, uint32_t size); +extern int32_t fifo_write(uint32_t id, void *data, int32_t len); +extern int32_t fifo_read(uint32_t id, void **data); +extern int32_t fifo_push(uint32_t id); +extern void fifo_show(uint32_t id); +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/hwgpio.h b/app/include/hwgpio.h new file mode 100644 index 0000000..f92c748 --- /dev/null +++ b/app/include/hwgpio.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * file include/common.h + * author YuLiang + * version 1.0.0 + * date 24-Nov-2021 + * brief This file provides all the headers of the gpio functions. + + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _HWGPIO_H_ +#define _HWGPIO_H_ + +/* Includes ------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ +#define GPIO_DIR_IN 1 +#define GPIO_DIR_OUT 2 + +#define GPIO_RUN 124 +#define GPIO_SYNC 22 +#define GPIO_SYNC_PPS 119 +#define GPIO_ERR1 123 +#define GPIO_ERR2 88 +#define GPIO_ALARM 18 + +#define GPIO_DAU1_POW 114 +#define GPIO_DAU2_POW 0 + +#define GPIO_CPLD_CS 86 +#define GPIO_CPLD_SCLK 85 +#define GPIO_CPLD_MOSI 4 +#define GPIO_CPLD_MISO 5 + +/* Exported types ------------------------------------------------------------*/ +/* 记录每个打开的gpio信息 */ +typedef struct +{ + uint16_t index; /* gpio在数组中的索引 */ + uint16_t gpio; /* gpio号 gpio2-15 = (2 - 1) * 32 + 15 = 47 */ + uint8_t is_export; /* 是否已导出 */ + uint8_t curr_dir; /* 方向当前值 1-in 2-out */ + uint8_t curr_val; /* 电平当前值 针对dir==out时 ,当dir为in时,每次都需要读文件 */ +} gpio_node_t; + +/* Exported macro ------------------------------------------------------------*/ +#define GPIO_DAU1_POW_ON gpio_val_set(gpio_dau1_pow_idx, 1) +#define GPIO_DAU1_POW_OFF gpio_val_set(gpio_dau1_pow_idx, 0) +#define GPIO_DAU2_POW_ON gpio_val_set(gpio_dau2_pow_idx, 1) +#define GPIO_DAU2_POW_OFF gpio_val_set(gpio_dau2_pow_idx, 0) + +#define GPIO_RUN_LED(_v_) gpio_val_set(gpio_run_idx, _v_) +#define GPIO_SYNC_LED(_v_) gpio_val_set(gpio_sync_idx, _v_) +#define GPIO_ERR1_LED(_v_) gpio_val_set(gpio_err1_idx, _v_) + +/* Extern global variables ---------------------------------------------------*/ +extern int32_t gpio_dau1_pow_idx; +extern int32_t gpio_dau2_pow_idx; +extern int32_t gpio_run_idx; +extern int32_t gpio_sync_idx; +extern int32_t gpio_err1_idx; + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t gpio_val_set(uint16_t gpio, uint8_t value); +extern int32_t gpio_val_get(uint16_t gpio, uint8_t *value); +extern int32_t gpio_dir_set(uint16_t gpio, uint8_t dir); +extern int32_t gpio_export(uint16_t gpio); +extern int32_t gpio_init(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/list.h b/app/include/list.h new file mode 100755 index 0000000..3c9e1f9 --- /dev/null +++ b/app/include/list.h @@ -0,0 +1,702 @@ +#ifndef _LIST_H_ +#define _LIST_H_ + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#define l_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - l_offsetof(type,member) );}) + + +static inline void prefetch(const void *x) {;} +static inline void prefetchw(const void *x) {;} + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + * + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && + (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, + struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * list_splice - join two lists, this is designed for stacks + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(const struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; prefetch(pos->next), pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + prefetch(pos->prev), pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + prefetch(pos->member.prev), &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; prefetch(pos->member.next), &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +/* + * Move a list from one list head to another. Fixup the pprev + * reference of the first entry if it exists. + */ +static inline void hlist_move_list(struct hlist_head *old, + struct hlist_head *new) +{ + new->first = old->first; + if (new->first) + new->first->pprev = &new->first; + old->first = NULL; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +#endif + diff --git a/app/include/memory.h b/app/include/memory.h new file mode 100644 index 0000000..70e2381 --- /dev/null +++ b/app/include/memory.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * file include/memory.h + * author YuLiang + * version 1.0.0 + * date 10-Sep-2021 + * brief This file provides all the headers of the memory functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +/* Includes ------------------------------------------------------------------*/ +#define MTYPE_MEMSTR_LEN 20 + +/* Define --------------------------------------------------------------------*/ +enum +{ + MTYPE_CLI = 1, + MTYPE_BUF, + MTYPE_BUF_DATA, + MTYPE_BUF_TMP, + MTYPE_VTY, + MTYPE_VTY_TMP, + MTYPE_VTY_OUT_BUF, + MTYPE_VTY_HIST, + MTYPE_LOG, + MTYPE_PREFIX, + MTYPE_THREAD_MONITOR, + MTYPE_MTIMER, + MTYPE_HASH, + MTYPE_HASH_BACKET, + MTYPE_HASH_INDEX, + MTYPE_THREAD, + MTYPE_THREAD_STATS, + MTYPE_THREAD_MASTER, + MTYPE_THREAD_FUNCNAME, + MTYPE_FIFO, + MTYPE_GPIO, + MTYPE_RECV, + MTYPE_SENT, + MTYPE_USART, + MTYPE_ETH, + MTYPE_GIS, + MTYPE_DAU, + MTYPE_CSG, + MTYPE_STORAGE, + MTYPE_DEBUG, + MTYPE_MAX, +}; + +/* Exported types ------------------------------------------------------------*/ +/* For pretty printing of memory allocate information. */ +typedef struct _mem_node +{ + int32_t index; + const char *format; +} mem_node_t; + +typedef struct _mem_list +{ + mem_node_t *nodes; + const char *name; +} mem_list_t; + +/* Exported macro ------------------------------------------------------------*/ +#define XMALLOC(mtype, size) \ + mtype_x_malloc (__FILE__, __LINE__, (mtype), (size)) +#define XMALLOC_Q(mtype, size) \ + mtype_x_malloc_q (__FILE__, __LINE__, (mtype), (size)) +#define XCALLOC(mtype, size) \ + mtype_x_calloc (__FILE__, __LINE__, (mtype), (size)) +#define XREALLOC(mtype, ptr, size) \ + mtype_x_realloc (__FILE__, __LINE__, (mtype), (ptr), (size)) +#define XFREE(mtype, ptr) \ + do { \ + mtype_x_free (__FILE__, __LINE__, (mtype), (ptr)); \ + ptr = NULL; } \ + while (0) +#define XSTRDUP(mtype, str) \ + mtype_x_strdup (__FILE__, __LINE__, (mtype), (str)) + +/* Extern global variables ---------------------------------------------------*/ + +/* Extern functions ----------------------------------------------------------*/ +extern void *mtype_x_malloc(const char *file, int32_t line, int32_t type, size_t size); +extern void *mtype_x_malloc_q(const char *file, int32_t line, int32_t type, size_t size); +extern void *mtype_x_calloc(const char *file, int32_t line, int32_t type, size_t size); +extern void *mtype_x_realloc(const char *file, int32_t line, int32_t type, void *ptr, size_t size); +extern void mtype_x_free(const char *file, int32_t line, int32_t type, void *ptr); +extern char *mtype_x_strdup(const char *file, int32_t line, int32_t type, const char *str); +extern void mtype_init(void); +extern void mtype_init_befor(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/mtimer.h b/app/include/mtimer.h new file mode 100644 index 0000000..6dd7786 --- /dev/null +++ b/app/include/mtimer.h @@ -0,0 +1,72 @@ +/***************************************************************************** + * file include/mtimer.h + * author YuLiang + * version 1.0.0 + * date 22-Sep-2021 + * brief This file provides all the headers of the timer functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __MTIMER_H__ +#define __MTIMER_H__ + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Define --------------------------------------------------------------------*/ +/* 以太网连接类型类型. */ +typedef enum +{ + MTIMER_S_STOP = 0, + MTIMER_S_RUN, +} MTIMER_STATE_E; + +/* Exported types ------------------------------------------------------------*/ +typedef void* (*MTIMER_CALLBACK)(void *arg); + +/* 定时器属性结构体. */ +typedef struct _mtimer_t +{ + MTIMER_CALLBACK handle; /* 回调函数 */ + void *arg; /* 运行参数 */ + char name[DEV_NAME_STR_LEN]; /* 定时器名字 */ + uint16_t interval; /* 间隔时间s */ + uint32_t time; /* 加入定时器的时间 */ +} mtimer_t; + +/* Extern global variables ---------------------------------------------------*/ + +/* Exported macro ------------------------------------------------------------*/ + +/* Exported functions --------------------------------------------------------*/ +extern int32_t mtimer_add(MTIMER_CALLBACK func, void *arg, uint16_t interval, char *name); +extern int32_t mtimer_init(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/include/pd_cpld.h b/app/include/pd_cpld.h new file mode 100644 index 0000000..348d8a3 --- /dev/null +++ b/app/include/pd_cpld.h @@ -0,0 +1,76 @@ +/****************************************************************************** + * file include/pd_cpld.h + * author YuLiang + * version 1.0.0 + * date 31-July-2023 + * brief This file provides all the headers of the cpld functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2023 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _PD_CPLD_H_ +#define _PD_CPLD_H_ + +/* Includes ------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ +#define CPLD_REG_V0 0x0 +#define CPLD_REG_V1 0x1 +#define CPLD_REG_STAT 0x3 +#define CPLD_REG_SYNC 0xb +#define CPLD_REG_WDG_EN 0xd +#define CPLD_REG_WDG_TM 0xe +#define CPLD_REG_WDG_CLR 0xf +#define CPLD_REG_MAX CPLD_REG_WDG_CLR + +/* Exported types ------------------------------------------------------------*/ +/* 设备配置的滤波类型. */ +typedef enum +{ + CPLD_SYNC_PT = 0, + CPLD_SYNC_OUTSIDE = 2 +} CPLD_SYNC_E; + +/* CPLD 版本结构体. */ +typedef struct +{ + uint16_t year; + uint8_t month; + uint8_t day; +} cpld_version_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t cpld_handle_init(void); +extern int32_t cpld_read(uint16_t addr, uint16_t len, uint16_t *data); +extern int32_t cpld_write(uint16_t addr, uint16_t len, uint16_t *data); +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/pd_csg.h b/app/include/pd_csg.h new file mode 100644 index 0000000..1a70ce9 --- /dev/null +++ b/app/include/pd_csg.h @@ -0,0 +1,215 @@ +/****************************************************************************** + * file include/pd_csg.h + * author YuLiang + * version 1.0.0 + * date 21-Feb-2023 + * brief This file provides all the headers of the csg server functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _PD_CSG_H_ +#define _PD_CSG_H_ + +#ifdef CFG_DEV_TYPE_LAND_PD +/* Includes ------------------------------------------------------------------*/ +#include "pd_main.h" + +/* Define --------------------------------------------------------------------*/ +#define CSG_DATA_FIFO "CSG_DATA_FIFO" +#define CSG_HEAD_LEN (22) +#define CSG_TAIL_LEN (4) +#define CSG_TOTLE_LEN (26) +#define CSG_PKT_LEN (1536) + +#define CSG_SHIQUCHA (28800) // 8 * 3600. + +#define CSG_CONNECT_REQ (0xA000) // 开机联络信息. +#define CSG_CONNECT_ACK (0xC000) // 开机联络信息应答. +#define CSG_HEARTBEAT_REQ (0xA001) // 心跳信息. +#define CSG_HEARTBEAT_ACK (0xC001) // 心跳信息应答. +#define CSG_ACITVE_CONNECT_REQ (0xC002) // 主站主动连接设备 . +#define CSG_REBOOT_REQ (0xC015) // 装置重启. + +#define CSG_FACTORY_SET_ACK (0xA010) // 生产初始化信息配置应答. +#define CSG_FACTORY_SET_REQ (0xC010) // 生产初始化信息配置. +#define CSG_FACTORY_GET_ACK (0xA020) // 生产初始化信息查询应答. +#define CSG_FACTORY_GET_REQ (0xC020) // 生产初始化信息查询. +#define CSG_CONFIG_SET_ACK (0xA013) // 下发参数配置应答. +#define CSG_CONFIG_SET_REQ (0xC013) // 下发参数配置. +#define CSG_CONFIG_GET_ACK (0xA023) // 查询参数配置应答. +#define CSG_CONFIG_GET_REQ (0xC023) // 查询参数配置. + +#define CSG_PORT_CONFIG_SET_REQ (0xC111) // 端口参数配置. +#define CSG_PORT_CONFIG_SET_ACK (0xA111) // 端口参数配置应答. +#define CSG_PORT_CONFIG_GET_REQ (0xC121) // 端口参数查询. +#define CSG_PORT_CONFIG_GET_ACK (0xA121) // 端口参数查询应答. + +#define CSG_EVENT_REQ (0xA130) // 事件主动上传. +#define CSG_EVENT_ACK (0xC136) // 事件回复确认. +#define CSG_TREND_REQ (0xC132) // 趋势数据查询. +#define CSG_TREND_ACK (0xA132) // 趋势数据主动上传. +#define CSG_REAL_IMAGE_REQ (0xC135) // 实时图谱数据平台召唤. +#define CSG_REAL_IMAGE_ACK (0xA135) // 实时图谱数据上传. + + +/* Exported types ------------------------------------------------------------*/ +/* 设备配置的滤波类型. */ +typedef enum +{ + CSG_FILTER_TYPE_FF = 1, + CSG_FILTER_TYPE_FR, + CSG_FILTER_TYPE_LF, + CSG_FILTER_TYPE_HF +} CSG_FILTER_TYPE_E; + +/* . */ +typedef struct +{ + int fd; // 后台通讯使用的 socket. + uint32_t pkt_index; // 报文索引. + int32_t data_fifo_id; // 数据处理线程发向后台发送线程的 fifo. + char buf_send[CSG_PKT_LEN]; + char buf_reply[CSG_PKT_LEN]; + char buf_recv[CSG_PKT_LEN]; + struct sockaddr_in server; + int32_t server_ip; // server ip. + uint16_t server_port; // server port. + uint8_t is_connect; // 是否连接上服务器. + uint8_t heartbeat; // 心跳发送后没有收到回复的次数. + int32_t communication_time; // 最后通讯时间. + pthread_mutex_t mutex; +} csg_t; + +/* 报文头结构. */ +typedef struct{ + uint16_t head; + uint16_t cmd; + uint8_t dev_num[PD_DEV_NUM_LEN]; + uint16_t len; +} csg_pkt_head_t; + +/* 报文尾结构.*/ +typedef struct{ + uint16_t checksum; + uint16_t tail; +} csg_pkt_tail_t; + +/* 心跳报文. */ +typedef struct{ + uint8_t port_alarm[PD_PORT_SUM]; // 通道报警状态 0: 没有报警 1: 报警. + uint32_t event_num[PD_PORT_SUM]; // 通道的最新事件编号. + uint32_t fault_num[PD_PORT_SUM]; // 通道的最新原始数据编号. + uint32_t utc; + float freq; // 实测同步源工频频率. + uint8_t dau_state[4]; // 采集模块的状态. + uint8_t out_sync; // 外接调频同步 1: 有效 0: 无效. + uint8_t pt_sync; // PT同步 1: 有效 0: 无效. + uint8_t in_sync; // 内同步 1: 有效 0: 无效. + uint8_t reserved1; // 预留 字节对齐. + uint8_t port_link_alarm[PD_PORT_SUM]; // 通道传感器连接状态 断线告警. + float voltage; // 设备电压, 预留. + float temperature; // 设备温度, 预留. + uint8_t heartbeat; // 心跳发送后没有收到回复的次数. + uint8_t bat; // 电池电量 预留, 单位 %. + uint8_t reserved2[18]; +} csg_pkt_heartbeat_t; + +typedef struct +{ + uint8_t vport; // 通道编号 1 ~ 16. + uint8_t is_concern; // 关注 1: 关注 0: 取消关注. + uint8_t r_noise_reduction; // 关联降噪 1: 关联噪声去噪. + uint8_t auto_noise_reduction; // 自适应降噪 自适应降噪. + uint8_t manual_noise_reduction; // 手动降噪 0 ~ 80 手动降噪. + uint8_t filter; // 滤波器类型. + uint8_t reserved[2]; //保留. +} csg_real_image_get_t; + +/* 实时图谱报文头 36Byte. */ +typedef struct{ + uint16_t index; // 每10包传完之后自动自加,用于区分当前包结束. + uint8_t pkt_sum; // 总包数 10. + uint8_t pkt_index; // 当前包数 0 ~ 9. + uint8_t vport; // 通道编号. + uint8_t reserved1[3]; + int16_t max; + int16_t avg; + uint32_t cnt; + uint32_t utc; + uint32_t is_sub; // 0: 定时数据主动上传 , 1: 定时数据 UTC 召唤. + uint32_t interval; // 上传间隔, 单位分钟. + uint8_t reserved2[8]; +} csg_real_image_t; + +typedef struct{ + uint32_t is_valid; // 1 有效 0 无效. + int16_t max; + int16_t avg; + uint32_t cnt; + float phase; // 放电相位. + uint32_t event_cnt; // 趋势数据中的的事件数量记录. + float noise; // 底噪平均值. + uint8_t reserved[16]; +} csg_trend_t; + +typedef struct{ + uint32_t idx; // 数据编号. + uint32_t utc; + uint32_t is_sub; // 是否是补包. + uint32_t interval; // 上传间隔 单位分钟. + csg_trend_t data[PD_PORT_SUM]; + uint8_t reserved[144]; +} csg_trend_head_t; + +typedef struct{ + uint16_t idx; // 每 10 包传完之后自动++, 用于区分当前包结束. + uint8_t pkt_sum; // 总包数 10. + uint8_t pkt_idx; // 当前包数 0 ~ 9. + uint8_t vport; // 通道编号. + uint8_t type; // 事件类型. + int16_t max; // 事件幅值最大值. + uint32_t level; // 事件等级. + uint32_t pd_Type; // 放电类型. + float type_per[8]; // 放电概率. + float PositionDistance; // 距离本传感器的距离. + uint32_t utc; // utc. + uint32_t event_idx; // 事件编号. + uint32_t cnt; // 脉冲频次计数值. +} csg_event_head_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ +extern csg_t csg; + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t csg_handle_init(void); +#endif +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/pd_dau.h b/app/include/pd_dau.h new file mode 100644 index 0000000..e2cd7fa --- /dev/null +++ b/app/include/pd_dau.h @@ -0,0 +1,297 @@ +/***************************************************************************** + * file include/pd_dau.h + * author YuLiang + * version 1.0.0 + * date 03-Feb-2023 + * brief This file provides all the headers of the dau functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __PD_DAU_H__ +#define __PD_DAU_H__ + +#ifdef CFG_DEV_TYPE_LAND_PD +/* Includes ------------------------------------------------------------------*/ +#include "pd_main.h" + +/* Define --------------------------------------------------------------------*/ +#define DAU_ADJ_POINT_SUM 10 + +#define DAU_PKT_LEN 1536 // DAU 发包最大数据长度. +#define DAU_PKT_PRPS_LEN 640 +#define DAU_PKT_PRPS_DATA_LEN 1280 + +#define DAU_REG_ADDR_GSCR 0x0000 // 全局寄存器 - 软件特征寄存器. +#define DAU_REG_ADDR_GSVR 0x0001 // 全局寄存器 - 软件版本寄存器. +#define DAU_REG_ADDR_GCR 0x0002 // 全局寄存器 - 控制寄存器. +#define DAU_REG_ADDR_GSR 0x0003 // 全局寄存器 - 状态寄存器. +#define DAU_REG_ADDR_GPFPCCR 0x0004 // 全局寄存器 - 工频周期时钟计数寄存器. +#define DAU_REG_ADDR_GPFPCR 0x0006 // 全局寄存器 - 工频周期计数寄存器. +#define DAU_REG_ADDR_GNSCR 0x0008 // 全局寄存器 - 纳秒级时钟计数寄存器. +#define DAU_REG_PORT_ADDR_MSRR 0x9000 // 端口状态寄存器 - 通道周波的原始采样值的均方根. +#define DAU_REG_PORT_ADDR_AVR 0x9008 // 端口状态寄存器 - 通道周波校准后的数值. + +#define DAU_GSVR_SECONDARY_Pos (0) +#define DAU_GSVR_SECONDARY_Msk (0xFF << DAU_GSVR_SECONDARY_Pos) /*!< 0x00FF */ +#define DAU_GSVR_PRIMARY_Pos (8) +#define DAU_GSVR_PRIMARY_Msk (0xFF << DAU_GSVR_PRIMARY_Pos) /*!< 0xFF00 */ + +#define DAU_GSR_SYNC_Pos (0) +#define DAU_GSR_SYNC_Msk (0x1 << DAU_GSR_SYNC_Pos) /*!< 0x0001 */ +#define DAU_GSR_MODE_Pos (1) +#define DAU_GSR_MODE_Msk (0x1 << DAU_GSR_MODE_Pos) /*!< 0x0002 */ + +#define DAU_GCR_MODE_Pos (0) +#define DAU_GCR_MODE_Msk (0x3 << DAU_GCR_MODE_Pos) /*!< 0x0003 */ + +#define DAU_REG_PORT_BASE 0x1000 // 端口寄存器基地址. +#define DAU_REG_PORT_OFFSET 0x1000 // 每个端口寄存器偏移地址. + +#define DAU_CR_SR_Pos (0) +#define DAU_CR_SR_Msk (0xFF << DAU_CR_SR_Pos) /*!< 0x00FF */ +#define DAU_CR_PT_Pos (12) +#define DAU_CR_PT_Msk (0xF << DAU_CR_PT_Pos) /*!< 0xF000 */ + +#define DAU_FTR_FT_Pos (0) +#define DAU_FTR_FT_Msk (0xF << DAU_FTR_FT_Pos) /*!< 0x000F */ + +/* Exported types ------------------------------------------------------------*/ +typedef void dau_prps_default_f(int16_t*); +typedef void dau_prps_process_f(uint8_t, uint8_t); +typedef void dau_trend_process_f(uint8_t, uint8_t, uint16_t); + +/* 消息队列类型. */ +typedef enum +{ + DAU_SEND_TYPE_REG_RD = 1, // 读取寄存器. + DAU_SEND_TYPE_REG_WR, // 写入寄存器. + DAU_SEND_TYPE_UPDATE, // FPGA 升级. + DAU_SEND_TYPE_COUNT +} DAU_SEND_TYPE_E; + +/* 共有命令字. */ +typedef enum +{ + DAU_PKT_CMD_RD = 0x01, + DAU_PKT_CMD_WR = 0x02, + DAU_PKT_CMD_SAV = 0x03, + DAU_PKT_CMD_UPD = 0x04, + DAU_PKT_CMD_PRPS = 0x81, +} DAU_PKT_CMD_E; + +/* 共有命令字. */ +typedef enum +{ + DAU_UPD_PROCESS = 0, + DAU_UPD_OK = 1, +} DAU_UPD_ERR_E; + +/* 和 DAU 通讯使用的报文结构体. */ +typedef struct +{ + uint8_t slave_id; + uint8_t func; + uint16_t addr; + uint16_t len; + uint16_t data[DAU_PKT_LEN]; +} dau_pkt_t; + +/* 和 DAU 通讯使用的报文结构体. */ +typedef struct +{ + uint32_t power_fre_cnt; + int16_t data[DAU_PKT_PRPS_LEN]; +} dau_pkt_prps_t; + +/* DAU 全局寄存器. */ +typedef struct +{ + uint16_t GSCR; // 全局软件特征寄存器 (Global Softwave Characteristic Register). + uint16_t GSVR; // 全局软件版本寄存器 (Global Softwave Version Register). + uint16_t GCR; // 全局控制寄存器 (Global Control Register). + uint16_t GSR; // 全局状态寄存器 (Global Status Register). + uint32_t GPFPCCR; // 全局工频周期时钟计数寄存器 (Global Power Frequency Period Clock Count Register). + uint32_t GPFPCR; // 全局工频周期计数寄存器 (Global Power Frequency Period Count Register). + uint32_t GNSCR; // 全局纳秒级时钟计数寄存器 (Global ns Count Register). +} dau_reg_t; + +/* DAU 通道寄存器. */ +typedef struct +{ + uint16_t CR; // 控制寄存器 (Control Register). + uint16_t NR; // 底噪寄存器 (Noise Register). + uint16_t ASPR[DAU_ADJ_POINT_SUM]; // 校准分段点寄存器 (Adjust Segmentation Register). + uint16_t AFAR[DAU_ADJ_POINT_SUM]; // 校准系数 a 寄存器 (Adjust Factor A Register). + uint16_t AFBR[DAU_ADJ_POINT_SUM]; // 校准系数 b 寄存器 (Adjust Factor B Register). + uint16_t FTR; // 滤波类型寄存器 (Filtering type Register). + uint16_t WTDR; // 波形触发延时间隔寄存器 (Waveform Trigger Delay Register). + uint16_t WTTR; // 波形触发阈值寄存器 (Waveform Trigger Threshold Register). + uint16_t WTPSR; // 波形触发记录采样点寄存器 (Waveform Trigger Ponit Sum Register). + uint16_t WTBPR; // 波形触发前记录采样点寄存器 (Waveform Trigger Befor Ponit Register). +} dau_port_reg_t; + +/* DAU 通道状态寄存器. */ +typedef struct +{ + uint16_t MSRR[PD_DAU_PORT_SUM]; // 通道周波的原始采样值的均方根 (Mean Square Root Register). + uint16_t AVR[PD_DAU_PORT_SUM]; // 通道周波校准后的数值 (Adjust Value Register). +} dau_port_state_reg_t; + +/* DAU 通道寄存器. */ +typedef struct +{ + uint32_t prps_cnt; // prps 工频周期计数. + uint32_t prps_index; // prps 计数. + int16_t prps[PD_PRPS_NUM]; // 1s prps 原始数据. + int16_t noise_level[PD_POWER_FRE]; // prps 计算使用的每个工频周期的噪声等级. + uint8_t is_complete; // prps 数据是否完整. +} dau_port_state_t; + +/* DAU 通道数据处理函数. */ +typedef struct +{ + dau_prps_default_f *data_default; // PRPS buffer 还原默认值. + dau_prps_process_f *denoise; // PRPS 自动 / 手动降噪. + dau_prps_process_f *denoise_r; // PRPS 关联降噪. + dau_prps_process_f *denoise_state; // PRPS 降噪数据统计. + dau_prps_process_f *denoise_real; // 实时数据自动 / 手动降噪. + dau_prps_process_f *denoise_real_r; // 实时数据关联降噪. + dau_prps_process_f *denoise_real_state; // PRPS 降噪数据统计. + dau_prps_process_f *event; // 事件计算. + dau_trend_process_f *trend; // 趋势数据. +} dau_port_func_t; + +/* DAU 板卡全局结构 */ +typedef struct{ + uint8_t unit; // FPGA 板卡号. + uint8_t port_num; // FPGA 板卡端口数量. + uint16_t ip_port; // 端口号. + int32_t ip_addr; // IP 地址. + uint16_t msg_send_type; // 发包线程需要处理的消息队列类型. + uint16_t send_type; // 当前发送的数据类型 DAU_SEND_TYPE_XXX. + uint8_t is_valid; // 用于配置保存. + uint8_t is_connect; // 记录 DAU 是否通讯正常. + dau_port_state_t port_state[PD_DAU_PORT_SUM]; // 端口状态. + dau_port_func_t *port_func[PD_DAU_PORT_SUM]; // 端口数据处理函数. + pthread_mutex_t mutex; // 收发线程同步使用. + dau_reg_t reg; // DAU 全局寄存器软标. + dau_reg_t reg_dau; // DAU 全局寄存器硬表. + dau_port_reg_t port_reg[PD_DAU_PORT_SUM]; // 通道寄存器软表. + dau_port_reg_t port_reg_dau[PD_DAU_PORT_SUM]; // 通道寄存器硬表. + dau_port_state_reg_t port_state_reg; // 通道状态寄存器. +} dau_t; + +/* DAU 全局结构 */ +typedef struct{ + int fd; // DAU 通讯使用的 socket. + int32_t recv_fifo_id; // 数据从接收线程发送到数据处理线程的 fifo. + uint32_t real_idx; + uint32_t trend_idx; + uint32_t event_index[PD_DAU_SUM][PD_DAU_PORT_SUM]; // 每个端口的事件索引. + uint32_t trend_event_index[PD_DAU_SUM][PD_DAU_PORT_SUM]; // 每个端口的趋势事件索引. + char recv_buf[DAU_PKT_LEN]; // DAU 收包 buffer. + int8_t is_update; // 是否开始升级. + int8_t update_ret; // 升级结果, 0 - 进行中, 1 - 成功, <0 - 失败. + uint16_t update_idx; // 升级报文索引. + uint32_t recv_cnt; + uint32_t recv_cnt_old; + uint32_t recv_err_cnt; + uint32_t recv_err_cnt_old; + uint32_t data_err_cnt; + uint32_t data_err_cnt_old; + uint32_t send_err_cnt; + uint32_t send_err_cnt_old; +} dau_ctrl_t; + +/* DAU 发送报文数据. */ +typedef struct _dau_send_msg_data_t +{ + uint16_t send_type; // 发送的数据类型. + uint16_t send_len; // 发送的数据长度. + void *data; // 数据的内存地址. +} dau_send_msg_data_t; + +/* DAU 消息队列要求的结构体样式. */ +typedef struct _dau_send_msg_t +{ + long type; // 消息类型, 用于收报文过滤. + dau_send_msg_data_t data; // 消息数据. +} dau_send_msg_t; + +/* DAU 接收报文数据. */ +typedef struct _dau_recv_msg_data_t +{ + uint32_t ip_addr; // 收包数据的源 IP 地址. + uint32_t recv_len; // 收包数据长度. + void *data; // 数据的内存地址. +} dau_recv_msg_data_t; + +/* DAU 消息队列要求的结构体样式. */ +typedef struct _dau_recv_msg_t +{ + long type; // 消息类型, 用于收报文过滤. + dau_recv_msg_data_t data; // 消息数据. +} dau_recv_msg_t; + +/* Exported macro ------------------------------------------------------------*/ +#define SET_BIT(REG, BIT) ((REG) |= (BIT)) +#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) +#define READ_BIT(REG, BIT) ((REG) & (BIT)) +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) +#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((REG) & (~(CLEARMASK))) | (SETMASK))) + +#define DAU_REG_PORT_ADDR_GET(port) ((port + 1) << 12) + +/* Extern global variables ---------------------------------------------------*/ +extern dau_t *dau[PD_DAU_SUM]; +extern dau_ctrl_t dau_ctrl; + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t dau_port_to_vport(uint8_t unit, uint8_t port); +extern int32_t dau_vport_to_port(uint8_t vport, uint8_t *unit, uint8_t *port); +extern int32_t dau_port_config_set(uint8_t unit, uint8_t port); +extern int32_t dau_port_type_set(uint8_t unit, uint8_t port, uint8_t type); +extern int32_t dau_handle_init(void); +extern uint8_t dau_power_frequency_state_get(void); +extern float dau_power_frequency_get(void); +extern uint8_t dau_connect_get(void); +extern void* dau_port_state_get(void *arg); +extern int32_t dau_reg_global_write(uint8_t unit); +extern int32_t dau_reg_port_write(uint8_t unit, uint8_t port); +extern int32_t dau_param_save(uint8_t unit); +extern void dau_shutdown(void); +extern int32_t dau_start(void); +extern int32_t dau_update(void); +extern void dau_show(uint8_t unit); +extern void dau_show_adj(uint8_t unit); +extern void dau_show_reg(uint8_t unit); +extern void dau_show_reg_port(uint8_t unit, uint8_t port); +#endif +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/include/pd_dbg.h b/app/include/pd_dbg.h new file mode 100755 index 0000000..0d29634 --- /dev/null +++ b/app/include/pd_dbg.h @@ -0,0 +1,134 @@ +/***************************************************************************** + * file include/pd_dbg.h + * author YuLiang + * version 1.0.0 + * date 01-June-2023 + * brief This file provides all the headers of the debug server functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __PD_DBG_H__ +#define __PD_DBG_H__ + +#ifdef CFG_DEV_TYPE_LAND_PD +/* Includes ------------------------------------------------------------------*/ +#include "pd_main.h" + +/* Define --------------------------------------------------------------------*/ +#define DEBUG_BUG_SIZE 1512 +#define DEBUG_CJSON_BUG_SIZE 1512 + +#define DEBUG_CONFIG_GET (0x0001) // 获取配置. +#define DEBUG_CONFIG_SET (0x0002) // 设置配置. +#define DEBUG_ALARM_CONFIG_GET (0x0003) // 获取事件告警二进制文件配置. +#define DEBUG_ALARM_CONFIG_SET (0x0004) // 设置事件告警二进制文件配置. +#define DEBUG_REBOOT (0x0005) // 重启. +#define DEBUG_TIME_SET (0x0006) // 对时. +#define DEBUG_NOISE_CAREFOR (0x0007) // 关注对应通道的底噪原始值, 校准底噪值. +#define DEBUG_NOISE_POST (0x0008) // 获取底噪原始值, 校准底噪值 服务端主动提交每秒刷新一次. +#define DEBUG_ADJSUT_COEFFICIENT_GET (0x0009) // 获取校准系数. +#define DEBUG_ADJSUT_COEFFICIENT_SET (0x000a) // 设置校准系数. +#define DEBUG_RUN_STATUS_GET (0x000b) // 获取运行状态数据. +#define DEBUG_ARM_UPGRADE (0x0050) // ARM 升级. +#define DEBUG_FPGA1_UPGRADE (0x0051) // FPGA 板卡升级. +#define DEBUG_FPGA2_UPGRADE (0x0052) // FPGA 板卡升级. +#define DEBUG_FPGA3_UPGRADE (0x0053) // FPGA 板卡升级. +#define DEBUG_FPGA4_UPGRADE (0x0054) // FPGA 板卡升级. +#define DEBUG_FPGA_UPGRADE (0x0055) // FPGA 板卡升级. +#define DEBUG_UPGRADE_ALL (0x0056) // ARM和FPGA板卡升级 + +#define BITMAP_SAVE_FILE (1 << 0) +#define BITMAP_IP_CHANGE (1 << 1) + +#define DEBUG_MANAGE_TOOL_PORT (10050) +/* Exported types ------------------------------------------------------------*/ +/* 端口校准系数. */ +typedef struct { + uint16_t point[4]; // 通道分段点. + uint16_t param_a[5]; // 通道线性度校准系数 a. + uint16_t param_b[5]; // 通道线性度校准系数 b. + uint16_t alarm_ref; // 断线告警比较值. + uint16_t reserve; +} debug_pkt_adj_vport_t; + +typedef struct { + debug_pkt_adj_vport_t vport[PD_PORT_SUM]; +} debug_pkt_adj_t; + +/* 端口状态, 主要是 ADC 原始值和校准后的值.*/ +typedef struct { + uint16_t value_adc; + int16_t value_adj; +} debug_pkt_port_state_t; + +typedef struct { + debug_pkt_port_state_t vport[PD_PORT_SUM]; +} debug_pkt_port_t; + +/* 设备状态. */ +typedef struct { + uint32_t idx; // 编号. + uint32_t UTC_TimeScale; // UTC 时标 + float F50Hz_Frequency; // 同步 50Hz 频率. + uint8_t F50Hz_SynStatus; // 同步状态. + uint8_t dau_status; // 插件状态. + uint16_t sensor_status; // 传感器断线状态. + uint16_t is_server_link; // 是否连接上后台. + uint16_t version; // 软件版本 + uint32_t communication_time; // 上一次通讯的时间 + uint32_t run_time; // 设备运行时间 +} debug_pkt_status_t; + +/* 报文头. */ +typedef struct{ + uint16_t head; // 0x55aa. + uint16_t cmd; // 命令. + uint32_t len; // 包长. +} debug_pkt_head_t; + +/* 调试工具全局结构 */ +typedef struct{ + int fd; // TCP server 监听使用的 socket. + int fd_client; // TCP client 通讯使用的 socket. + char buf[DEBUG_BUG_SIZE]; // 通讯使用收法包 buffer. + char buf_post[DEBUG_BUG_SIZE]; // 主动上传通讯使用收法包 buffer. + int is_manual_col; // 是否手动采集. +} debug_ctrl_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ +extern debug_ctrl_t debug_ctrl; + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t debug_handle_init(void); +extern int32_t debug_pkt_port_state_post(void); +#endif +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/include/pd_main.h b/app/include/pd_main.h new file mode 100755 index 0000000..0ae609c --- /dev/null +++ b/app/include/pd_main.h @@ -0,0 +1,339 @@ +/***************************************************************************** + * file include/pd_main.h + * author YuLiang + * version 1.0.0 + * date 07-Feb-2023 + * brief This file provides all the headers of the partial discharge functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __PD_MAIN_H__ +#define __PD_MAIN_H__ + +#ifdef CFG_DEV_TYPE_LAND_PD +/* Includes ------------------------------------------------------------------*/ +#include "cmd.h" + +/* Define --------------------------------------------------------------------*/ +#define PD_DAU_SUM 2 +#define PD_DAU_PORT_SUM 8 +#define PD_PORT_SUM 16 +#define PD_PORT_PROMPT_LEN 64 // DAU 端口节点前标长度. + +#define PD_DEV_NUM_LEN 16 +#define PD_DEV_TYPE_LEN 8 + +#define PD_POWER_FRE 50 +#define PD_PHASE_NUM 128 +#define PD_PRPS_NUM 6400 +#define PD_PRPS_DATA_LEN 12800 + +#define PD_GIS_MIN_VALUE (-800) // 特高频局放的采集数值下限 -800 (-80.0dBm). + +#define DEBUG_DAU_FILE "PDMonitor_DAU" +#define UPGRADE_SOFTWARE "upgrade.sw" + +/* Exported types ------------------------------------------------------------*/ +/* 用于命令行模式节点注册配置保存函数 */ +typedef int pd_port_cmd_save_config_f(vty_t*, uint8_t, uint8_t); + +/* 向服务器发送消息的类型. */ +typedef enum +{ + PD_SEND_TYPE_PRPS = 1, + PD_SEND_TYPE_TREND, + PD_SEND_TYPE_EVENT, + PD_SEND_TYPE_COUNT +} PD_SEND_TYPE_E; + +/* 端口类型. */ +typedef enum +{ + PD_PORT_TYPE_UHF = 1, + PD_PORT_TYPE_AE, + PD_PORT_TYPE_TEV, + PD_PORT_TYPE_HF, + PD_PORT_TYPE_COUNT +} PD_PORT_TYPE_E; + +/* 设备配置的主备类型. */ +typedef enum +{ + PD_DAU_MODE_AUTO = 0, + PD_DAU_MODE_MASTER, + PD_DAU_MODE_SLAVE, + PD_DAU_MODE_COUNT +} PD_DAU_MODE_E; + +/* 设备配置的滤波类型. */ +typedef enum +{ + PD_FILTER_TYPE_HF = 3, + PD_FILTER_TYPE_LF = 6, + PD_FILTER_TYPE_FR = 9, + PD_FILTER_TYPE_FF = 12 +} PD_FILTER_TYPE_E; + +/* 端口类型. */ +typedef enum +{ + PD_SEN_TYPE_NONE = 0, + PD_SEN_TYPE_SIG, + PD_SEN_TYPE_NOISE, + PD_SEN_TYPE_SIG_NOISE, + PD_SEN_TYPE_COUNT +} PD_SEN_TYPE_E; + +/* 设备配置的滤波类型. */ +typedef enum +{ + PD_SYNC_PT = 1, + PD_SYNC_POWER = 2, + PD_SYNC_OUTSIDE = 4, +} PD_SYNC_MODE_E; + +/* 端口节点配置优先级 */ +typedef enum +{ + PD_PORT_CMD_PRI_DAU = 0, + PD_PORT_CMD_PRI_COUNT +} PD_PORT_CMD_PRI_E; + +/* 1s prps 数据结构体. */ +typedef struct { + int16_t avg_o; // 通道原始信号 (降噪前) 的平均值. + int16_t max; // 通道的最大值. + int16_t avg; // 通道的平均值. + uint32_t cnt; // 通道的计数值. + int32_t phase_sum[PD_PHASE_NUM]; // 通道的周波放电相位累加值. + int16_t phase_max[PD_PHASE_NUM]; // 通道的周波相位最大值. + int16_t phase_avg[PD_PHASE_NUM]; // 通道的周波相位平均值. + uint16_t phase_cnt[PD_PHASE_NUM]; // 通道的周波相位计数值 . + int16_t data[PD_PRPS_NUM]; // 通道数据. + uint8_t is_valid; // prps 数据是否有效. +} pd_prps_data_t; + +typedef struct { + uint32_t index; // 数据编号. + uint32_t utc; // UTC 时标. + uint32_t is_denoise; // 是否降噪的标记: 1-已经降噪 0-原始没有降噪. + void *reserve; // 与 GIS5.0 设备的结构体同步. + pd_prps_data_t data[PD_DAU_SUM][PD_DAU_PORT_SUM]; // 16 个通道的数据. +} pd_prps_t; + +typedef struct { + uint32_t index; // 数据编号: 0 - (2^32-1) 循环. + uint32_t utc; // UTC 时标. + + uint8_t vport; // 通道编号. + uint8_t level; // 事件等级. + uint8_t type; // 放电类型识别结果: 每种放电类型的比较的概率. + uint8_t second; // 当前事件记录的时间秒数. + + int16_t max; // 通道的最大值. + int32_t avg; // 通道的平均值. + int32_t cnt; // 通道的计数值. + float type_per[8]; // 放电类型识别结果. + + uint16_t PositionEfficiency; // 定位信息: 有效性 0: 无效 1:有效. + uint16_t PositionOtherChNum; // 定位信息: 对端传感器通道号 范围 [1-16]. + uint32_t PositionDistance; // 定位信息: 距离本传感器的距离. + uint32_t RatioOutValue; // 定位信息: 实际比率 配对上 / 超过阀值的计数. + + int32_t phase_sum[PD_PHASE_NUM]; // 通道的周波相位累加值. + int16_t phase_max[PD_PHASE_NUM]; // 通道的周波相位最大值. + int16_t phase_avg[PD_PHASE_NUM]; // 通道的周波相位平均值. + int32_t phase_cnt[PD_PHASE_NUM]; // 通道的周波相位计数值. + + int16_t data[PD_PRPS_NUM]; // 通道 n 的测量数据. +} pd_event_t; + +typedef struct { + int16_t max; // 通道的最大值. + int64_t avg; // 通道的平均值. + int32_t cnt; // 通道的计数值. + float phase; // 放电相位 . + float noise; // 趋势数据中的底噪值: 单位 dBm . + uint32_t event_cnt; // 趋势数据中的的事件数量记录. + int64_t phase_sum[PD_PHASE_NUM]; // 通道的周波放电相位累加值. + int16_t phase_max[PD_PHASE_NUM]; // 通道的周波相位最大值. + int16_t phase_avg[PD_PHASE_NUM]; // 通道的周波相位平均值. + int32_t phase_cnt[PD_PHASE_NUM]; // 通道的周波相位计数值. +} pd_trend_data_t; + +typedef struct { + uint32_t index; // 数据编号: 0 - (2^32-1) 循环. + uint32_t utc; // UTC 时标. + pd_trend_data_t data[PD_DAU_SUM][PD_DAU_PORT_SUM]; // 通道 0 - 15 的计算及测量数据. +} pd_trend_t; + +typedef struct { + pd_prps_t *denoise; // PRPS 数据指针. + pd_prps_t real; // 实时数据指针. + pd_event_t event[PD_DAU_SUM][PD_DAU_PORT_SUM]; // 端口事件指针. + pd_trend_t trend; // 趋势数据. +} pd_data_t; + +typedef struct { + uint32_t type; + void *data; +} pd_csg_msg_t; + +typedef struct { + uint8_t vport; // 通道编号. + uint8_t filter; // 滤波器类型 1: 低频段 2: 全频段 3: 窄频段 4: 高频段 + uint8_t sensor_type; // 0: 无配置; 1: UHF信号传感器; 2: UHF噪声传感器 ; 3: UHF信号传感器, 关联噪声降噪. + uint8_t phase_sequence; // 对应相序 1A 2B 3C. + + uint16_t NearSensor1_Number; //关联监测点1 + uint16_t NearSensor1_Distance; //与监测点1距离 + uint16_t Signal1_AttenuationRatio; //衰减 dBm/m 20200717 + uint16_t NearSensor2_Number; //关联监测点2 + + uint16_t NearSensor2_Distance; //与监测点2距离 + uint16_t Signal2_AttenuationRatio; //衰减 dBm/m 20200717 + uint16_t NearSensor3_Number; //关联监测点3 + uint16_t NearSensor3_Distance; //与监测点3距离 + + uint16_t Signal3_AttenuationRatio; //衰减 dBm/m 20200717 + uint16_t NearSensor4_Number; //关联监测点4 + uint16_t NearSensor4_Distance; //与监测点4距离 + uint16_t Signal4_AttenuationRatio; //衰减 dBm/m 20200717 + + int16_t event_threshold; // 事件触发阀值 0.1dBm. + uint16_t event_counter; // 事件次数阀值. + int16_t env_noise; // 环境底噪, 单位: dBm 0~100 100-自动降噪. + int16_t noise_reduction; // 降噪水平, 单位: 0.1dBm. + + int16_t UpperLimitOfDischargeAmplitude; //放电幅值上限 未使用 + int16_t WaveTriggerThreshold; //波形触发阀值 (-32768~32768) + uint16_t PrescalerSampleValue; //通道采样抽点数值 未使用 + uint16_t reserved1; + + uint32_t SamplePointsTotalTriggered; //脉冲波形一次完整记录的采样点数 未使用 + uint32_t RecordStartAdr; //波形记录:本通道的SDR/DDR的缓冲区起始地址 未使用 + uint32_t RecordStopAdr; //波形记录:本通道的SDR/DDR的缓冲区结束地址 未使用 + uint32_t sample_rate; //数据采样频率 Hz + uint32_t BeforeTriggerTime; //触发前采样时间 10ns + uint32_t AfterTriggerTime; //触发后采样时间 10ns + uint32_t storage_event; //事件存储文件数量阈值 + uint32_t storage_wave; //波形存储文件数量阈值 + uint8_t reserved[4]; //预留 +}pd_port_config_old_t; + +typedef struct{ + uint8_t power_frequency; // 工频频率 未使用 + uint8_t sync_mode; // 同步方式 1: PT 同步 2: 电源同步(默认) 4: 外接信号同步. + uint8_t TimingSyncMode; // 对时方式 1:网卡对时 2:GPS对时 4:外部对时 8:FPGA内部对时 + uint8_t real_period; // 定时数据上送周期, 单位: 分钟. + uint8_t trend_period; // 趋势数据上送周期, 单位: 分钟. + uint8_t heartbeat_period; // 心跳包周期. + uint16_t NumberWindowsOfPhase; // 相位窗数 //未使用 + uint32_t SyncTime; // 对时数据 //未使用 + uint16_t AlarmPeriod; // 报警观察周期 //未使用 + uint16_t TimePrpsStrategy; // 定时数据存储策略 0 全部存储 1 未上传存储. + uint32_t storage_trend; // 趋势存储文件数量阈值. + uint32_t StorageAlarm; // 告警存储文件数量阈值. + uint32_t StorageRun; // 运行状态存储文件数量阈值. + uint32_t storage_real; // 定时PRPS存储文件数量阈值. + uint32_t limit_event_time; // 事件频繁判断时间. + uint32_t limit_event_cnt; // 事件频繁判断个数. + uint32_t limit_event_interval; // 事件频繁后存储间隔. + uint32_t is_prps_save; // 实时PRPS存储使能. + uint8_t Reserved[52]; // 预留 . +}pd_config_old_t; + +/* 局放配置. */ +typedef struct { + pd_port_config_old_t config; // 为了兼容原先的协议. + uint8_t is_concern; // 是否被关注, 在实时波形中使用.. + uint8_t r_noise_reduction; // 是否启动关联降噪, 在实时波形中使用.. + uint8_t auto_noise_reduction; // 是否自动降噪, 在实时波形中使用.. + uint8_t manual_noise_reduction; // 手动降噪, 单位: 0.1dBm, 在实时波形中使用.. + uint8_t filter_cfg; // 端口配置的滤波类型, 在实时波形中使用. +} pd_port_config_t; + +typedef struct{ + uint8_t dev_num[PD_DEV_NUM_LEN]; // 设备编号. + uint8_t dev_type[PD_DEV_TYPE_LEN]; // 设备型号. + uint32_t factory_date; // 出厂日期. + uint32_t deployment_date; // 部署日期. + uint32_t ipv4; // 本机 IP. + uint8_t ipv6[16]; // 预留 IPV6 , 未使用. + uint8_t mac[MAC_ADDR_LEN]; // MAC地址. + uint8_t LocalIP2[4]; // 本机 IP, 未使用. + uint8_t LocalIP2_V6[16]; // 预留 IPV6 , 未使用. + uint8_t LocalMacAddress2[6]; // MAC 地址, 未使用. + uint32_t server_ipv4; // 服务器 IP. + uint8_t server_ipv6[16]; // 预留 IPV6, 未使用. + uint8_t ServerIP2[4]; // 服务器 IP, 未使用. + uint8_t ServerIP2_V6[16]; // 预留 IPV6 , 未使用. + uint16_t server_port; // 服务器端口号. + uint16_t ServerIP2_Port; // 服务器端口号, 未使用. + uint8_t port_type[PD_PORT_SUM]; // 采集通道类型 , 1 表示特高频局放 2 表示超声局放 3 表示 TEV 4 表示高频. + uint8_t Reserved[12]; // 预留. + uint32_t runTime; // 运行时间, 未使用. + uint32_t devVoltage; // 设备电压, 未使用. + uint32_t devTemperature; // 设备温度, 未使用. + uint32_t devSleepTime; // 设备休眠时长, 未使用. + uint32_t devPasswd; // 设备密码, 未使用. + uint8_t batteryVoltage; // 电池电压, 未使用. + uint8_t linkStatus; // 通讯状态, 未使用. +}pd_factory_old_t; + +typedef struct { + pd_factory_old_t factory; + pd_config_old_t config; + pd_port_config_t port_config[PD_DAU_SUM][PD_DAU_PORT_SUM]; // 端口配置. + uint32_t concern_bitmap; +} pd_config_t; + +typedef struct { + uint8_t sync; +} pd_state_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ +extern pd_data_t pd_data; +extern pd_config_t pd_config; +extern pd_state_t pd_state; +extern cmd_node_t pd_port_node; + +/* Extern functions ----------------------------------------------------------*/ +extern int32_t pd_main(void); +extern int32_t pd_port_cmd_config_register(int32_t pri, pd_port_cmd_save_config_f *func); +extern void pd_sync_mode_set(void); +extern void pd_sync_state_get(void); +extern void pd_wdg_clr(void); +extern void pd_prps_show(void); +extern void pd_show(void); +#endif +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/include/pd_storage.h b/app/include/pd_storage.h new file mode 100644 index 0000000..198a51d --- /dev/null +++ b/app/include/pd_storage.h @@ -0,0 +1,54 @@ +#ifndef __PD_STORAGE_H_ +#define __PD_STORAGE_H_ + +/* Includes ------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ +#define STORAGE_DATA_FIFO "STORAGE_DATA_FIFO" + +/* Exported types ------------------------------------------------------------*/ +/*本地存储类型*/ +typedef enum +{ + STORAGE_TYPE_EVENT = 1, + STORAGE_TYPE_TREND, + STORAGE_TYPE_ALARM, + STORAGE_TYPE_RUNSTATUS, + STORAGE_TYPE_FIXEDTIMERPRPS +}LOCAL_STORAGE_TYPE_E; + +/* */ +typedef struct +{ + int32_t storage_fifo_id; // 存储FIFO ID + pthread_mutex_t mutex; +} storage_t; + +typedef struct { + uint32_t type; + void *data; +} pd_storage_msg_t; + +typedef struct { + uint16_t event_cnt[PD_DAU_SUM][PD_DAU_PORT_SUM]; //事件文件已存储数 + uint16_t trend_cnt; //趋势文件已存储数 + uint16_t fixedtimePRPS_cnt; //定时PRPS文件已存储数 + uint32_t event_max_utc[PD_DAU_SUM][PD_DAU_PORT_SUM]; + uint32_t event_min_utc[PD_DAU_SUM][PD_DAU_PORT_SUM]; + uint32_t trend_max_utc; + uint32_t trend_min_utc; + uint32_t fixedtimePRPS_max_utc; + uint32_t fixedtimePRPS_min_utc; + uint32_t utc_base[PD_DAU_SUM][PD_DAU_PORT_SUM]; //事件文件存储频繁判断基准值 + uint32_t limit_cnt[PD_DAU_SUM][PD_DAU_PORT_SUM]; //事件文件存储频繁判断个数 +} storage_arg_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ +extern storage_t storage; +/* Extern functions ----------------------------------------------------------*/ +extern int32_t localstorage_handle_init(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ \ No newline at end of file diff --git a/app/include/process.h b/app/include/process.h new file mode 100644 index 0000000..2a043e4 --- /dev/null +++ b/app/include/process.h @@ -0,0 +1,60 @@ +/***************************************************************************** + * file include/process.h + * author YuLiang + * version 1.0.0 + * date 26-Sep-2021 + * brief This file provides all the headers of the process functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __PROCESS_H__ +#define __PROCESS_H__ + +/* Includes ------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ + +/* Exported types ------------------------------------------------------------*/ +/* 消息队列类型. */ +typedef enum +{ + MSG_TYPE_DAU_RECV = 5, /* 1-4 固定被 DAU 发送线程使用, 这里从 5 开始. */ + MSG_TYPE_COUNT, +} MSG_TYPE_E; + +/* Extern global variables ---------------------------------------------------*/ +extern int32_t recv_qid; +extern uint16_t version_hex; +extern uint32_t start_time; + +/* Exported macro ------------------------------------------------------------*/ + +/* Exported functions --------------------------------------------------------*/ +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/include/sockunion.h b/app/include/sockunion.h new file mode 100644 index 0000000..f9aebbf --- /dev/null +++ b/app/include/sockunion.h @@ -0,0 +1,230 @@ +/****************************************************************************** + * file include/sockunion.h + * author YuLiang + * version 1.0.0 + * date 09-Oct-2021 + * brief This file provides all the headers of the sockunion functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _SOCKUNION_H_ +#define _SOCKUNION_H_ + +/* Includes ------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ +#define IPV4_MAX_BITLEN 32 +#define IPV6_MAX_BITLEN 128 + +#define PNBBY 8 +#define AFI_IP 1 +#define AFI_IP6 2 +#define AFI_MAX 3 + +#define SU_ADDRSTRLEN 46 +/* Default address family. */ +#ifdef HAVE_IPV6 +#define AF_INET_UNION AF_INET6 +#else +#define AF_INET_UNION AF_INET +#endif + +/* Exported types ------------------------------------------------------------*/ +/* IPv4 and IPv6 unified prefix structure. */ +typedef struct _prefix +{ + uint8_t family; + uint8_t prefixlen; + union + { + uint8_t prefix; + struct in_addr prefix4; +#ifdef HAVE_IPV6 + struct in6_addr prefix6; +#endif /* HAVE_IPV6 */ + struct + { + struct in_addr id; + struct in_addr adv_router; + } lp; + uint8_t val[8]; + } u __attribute__ ((aligned (8))); +} prefix_t; + +/* IPv4 prefix structure. */ +typedef struct _prefix_ipv4 +{ + uint8_t family; + uint8_t prefixlen; + struct in_addr prefix __attribute__ ((aligned (8))); +} prefix_ipv4_t; + +/* IPv6 prefix structure. */ +#ifdef HAVE_IPV6 +typedef struct _prefix_ipv6 +{ + uint8_t family; + uint8_t prefixlen; + struct in6_addr prefix __attribute__ ((aligned (8))); +} prefix_ipv6_t; +#endif /* HAVE_IPV6 */ + +typedef enum +{ + ACCESS_TYPE_STRING, + ACCESS_TYPE_NUMBER +} ACCESS_TYPE_E; + +/* Filter type is made by `permit', `deny' and `dynamic'. */ +typedef enum +{ + FILTER_DENY, + FILTER_PERMIT, + FILTER_DYNAMIC +} FILTER_TYPE_E; + +/* List of access_list. */ +typedef struct +{ + struct _access_list *head; + struct _access_list *tail; +} access_list_list_t; + +/* Master structure of access_list. */ +typedef struct +{ + /* List of access_list which name is number. */ + access_list_list_t num; + + /* List of access_list which name is string. */ + access_list_list_t str; + + /* Hook function which is executed when new access_list is added. */ + void (*add_hook)(struct _access_list *); + + /* Hook function which is executed when access_list is deleted. */ + void (*delete_hook)(struct _access_list *); +} access_master_t; + +typedef struct +{ + /* Cisco access-list */ + int extended; + struct in_addr addr; + struct in_addr addr_mask; + struct in_addr mask; + struct in_addr mask_mask; +} filter_cisco_t; + +typedef struct +{ + /* If this filter is "exact" match then this flag is set. */ + int exact; + + /* Prefix information. */ + prefix_t prefix; +} filter_zebra_t; + +/* Filter element of access list */ +typedef struct _filter +{ + /* For doubly linked list. */ + struct _filter *next; + struct _filter *prev; + + /* Filter type information. */ + FILTER_TYPE_E type; + + /* Cisco access-list */ + int cisco; + + union + { + filter_cisco_t cfilter; + filter_zebra_t zfilter; + } u; +} filter_t; + +/* Access list */ +typedef struct _access_list +{ + char *name; + char *remark; + + access_master_t *master; + + ACCESS_TYPE_E type; + + struct _access_list *next; + struct _access_list *prev; + + filter_t *head; + filter_t *tail; +} access_list_t; + +typedef union +{ + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif /* HAVE_IPV6 */ +} SOCKUNION_U; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ + +/* Extern functions ----------------------------------------------------------*/ +extern void prefix_free(prefix_t *p); +extern prefix_ipv4_t *prefix_ipv4_new(void); +#ifdef HAVE_IPV6 +extern prefix_ipv6_t *prefix_ipv6_new(void); +#endif /* HAVE_IPV6 */ + +extern access_list_t *access_list_lookup(uint16_t afi, const char *name); +extern FILTER_TYPE_E access_list_apply(access_list_t *access, void *object); +extern void masklen2ip(int masklen, struct in_addr *netmask); +extern int prefix_match(const prefix_t *n, const prefix_t *p); + +extern int str2sockunion(const char *str, SOCKUNION_U *su); +extern const char *sockunion2str(SOCKUNION_U *su, char *buf, size_t len); +extern int sockunion_accept(int sock, SOCKUNION_U *su); +extern int set_nonblocking(int fd); +extern prefix_t *sockunion2hostprefix(const SOCKUNION_U *su); +extern char *sockunion_su2str(SOCKUNION_U *su); +extern int sockunion_stream_socket(SOCKUNION_U *su); +extern int sockunion_reuseaddr(int sock); +extern int sockunion_bind(int sock, SOCKUNION_U *su, unsigned short port, SOCKUNION_U *su_addr); +extern int sockunion_ip_set(char *name, unsigned int addr); +extern int sockunion_mask_set(char *name, unsigned int mask); +extern int sockunion_gw_set(char *name, unsigned int gateway, unsigned int gateway_old); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/include/thread_monitor.h b/app/include/thread_monitor.h new file mode 100644 index 0000000..d12c949 --- /dev/null +++ b/app/include/thread_monitor.h @@ -0,0 +1,64 @@ +/***************************************************************************** + * file include/thread_monitor.h + * author YuLiang + * version 1.0.0 + * date 08-Oct-2021 + * brief This file provides all the headers of the thread monitor functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __THREAD_M_H__ +#define __THREAD_M_H__ + +/* Includes ------------------------------------------------------------------*/ +#include + +/* Define --------------------------------------------------------------------*/ +#define THREAD_M_NAME_LEN 32 + +/* Exported types ------------------------------------------------------------*/ +/* 线程监控结构体. */ +typedef struct _thread_m_t +{ + char name[THREAD_M_NAME_LEN]; /* 线程名字 */ + pthread_t pid; /* 线程pid */ + int8_t alive; /* 是否活着 */ +} thread_m_t; + +/* Extern global variables ---------------------------------------------------*/ + +/* Exported macro ------------------------------------------------------------*/ + +/* Exported functions --------------------------------------------------------*/ +extern int32_t thread_m_add(char *str, pthread_t pid); +extern int32_t thread_m_init(void); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ + diff --git a/app/include/vty.h b/app/include/vty.h new file mode 100644 index 0000000..e58340f --- /dev/null +++ b/app/include/vty.h @@ -0,0 +1,331 @@ +/****************************************************************************** + * file include/vty.h + * author YuLiang + * version 1.0.0 + * date 10-Sep-2021 + * brief This file provides all the headers of the vty functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef _VTY_H_ +#define _VTY_H_ + +/* Includes ------------------------------------------------------------------*/ +#include + +#include "array.h" + +/* Define --------------------------------------------------------------------*/ +/* Thread types. */ +#define THREAD_READ 0 +#define THREAD_WRITE 1 +#define THREAD_TIMER 2 +#define THREAD_EVENT 3 +#define THREAD_READY 4 +#define THREAD_BACKGROUND 5 +#define THREAD_UNUSED 6 +#define THREAD_PERFORM 7 + +/* Struct timeval's tv_usec one second value. */ +#define TIMER_SECOND_MICRO 1000000L + +#define RUSAGE_T rusage_t +#define GETRUSAGE(X) thread_getrusage(X) + +#define THREAD_ADD_READ(m,f,a,v) thread_add_read(m,f,a,v,#f) +#define THREAD_ADD_WRITE(m,f,a,v) thread_add_write(m,f,a,v,#f) +#define THREAD_ADD_TIMER(m,f,a,v) thread_add_timer(m,f,a,v,#f) +#define THREAD_ADD_TIMER_MSEC(m,f,a,v) thread_add_timer_msec(m,f,a,v,#f) +#define THREAD_ADD_EVENT(m,f,a,v) thread_add_event(m,f,a,v,#f) +#define THREAD_EXECUTE(m,f,a,v) thread_execute(m,f,a,v,#f) + +/* Macros. */ +#define THREAD_ARG(X) ((X)->arg) +#define THREAD_FD(X) ((X)->u.fd) +#define THREAD_VAL(X) ((X)->u.val) + +#define VTY_BUFSIZ 512 +#define VTY_TMP_BUFSIZ 1024 +#define VTY_MAXHIST 20 +#define TELNET_NAWS_SB_LEN 5 + +#define VTY_TIMEOUT_VAL 600 + +/* Vty read buffer size. */ +#define VTY_READ_BUFSIZ 512 + +/* Small macro to determine newline is newline only or linefeed needed. */ +#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n") + +#define CONTROL(X) ((X) - '@') +#define VTY_NO_ESCAPE 0 +#define VTY_PRE_ESCAPE 1 +#define VTY_ESCAPE 2 + +#define VTY_USERNAME_LEN 32 +#define VTY_USERNAME_DEFAULT "ld" +#define VTY_PASSWORD_DEFAULT "1" + +/* Exported types ------------------------------------------------------------*/ +/* buffer数据结构体. 必须保证: 0 <= sp <= cp <= size. */ +typedef struct _buf_data +{ + struct _buf_data *next; /* 数据链表. */ + size_t cp; /* 当前可以添加的数据索引. */ + size_t sp; /* 等待数据发送的数据索引. */ + unsigned char data[]; /* 数据空间. */ +} buf_data_t; + +/* Buffer结构体. */ +typedef struct _buf +{ + buf_data_t *head; /* 数据块头. */ + buf_data_t *tail; /* 数据块尾. */ + size_t size; /* 每个数据块的大小. */ +} buf_t; + +typedef int hash_key_f(void*); +typedef void* hash_alloc_f(void *); +typedef int hash_cmp_f(const void*, const void*); + +typedef struct _hash_backet +{ + struct _hash_backet *next; /* Linked list. */ + unsigned int key; /* Hash key. */ + void *data; /* Data. */ +} hash_backet_t; + +typedef struct _hash +{ + hash_backet_t **index; /* Hash backet. */ + unsigned int size; /* Hash table size. */ + hash_key_f *hash_key; /* Key make function. */ + hash_cmp_f *hash_cmp; /* Data compare function. */ + unsigned long count; /* Backet alloc. */ +} hash_t; + +typedef unsigned char thread_type; + +typedef struct _rusage +{ + struct rusage cpu; + struct timeval real; +} rusage_t; + +typedef struct _time_stats +{ + unsigned long total, max; +} time_stats_t; + +typedef struct _cpu_thread_history +{ + int (*func)(void*); + char *funcname; + unsigned int total_calls; + time_stats_t real; + time_stats_t cpu; + thread_type types; +} cpu_thread_history_t; + + + +/* Linked list of thread. */ +typedef struct _thread_list +{ + struct _thread *head; + struct _thread *tail; + int count; +} thread_list_t; + +/* Master of the theads. */ +typedef struct _thread_master +{ + thread_list_t read; + thread_list_t write; + thread_list_t timer; + thread_list_t event; + thread_list_t ready; + thread_list_t unuse; + thread_list_t background; + fd_set readfd; + fd_set writefd; + fd_set exceptfd; + unsigned long alloc; +} thread_master_t; + +/* Thread itself. */ +typedef struct _thread +{ + thread_type type; /* thread type */ + thread_type add_type; /* thread type */ + struct _thread *next; /* next pointer of the thread */ + struct _thread *prev; /* previous pointer of the thread */ + thread_master_t *master; /* pointer to the struct thread_master. */ + int (*func)(struct _thread*); /* event function */ + void *arg; /* event argument */ + union + { + int val; /* second argument of the event. */ + int fd; /* file descriptor in case of read/write. */ + struct timeval sands; /* rest of time sands value. */ + } u; + RUSAGE_T ru; /* Indepth usage info. */ + cpu_thread_history_t *hist; /* cache pointer to cpu_history */ + char* funcname; +} thread_t; + +typedef int thread_func_f(thread_t*); + +typedef enum {VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV} VTY_TYPE_E; +typedef enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE} VTY_STAT_E; + +/* Vty events */ +typedef enum +{ + VTY_SERV, + VTY_READ, + VTY_WRITE, + VTY_TIMEOUT_RESET, +#if 0 + VTYSH_SERV, + VTYSH_READ, + VTYSH_WRITE +#endif /* VTYSH */ +} VTY_EVENT_E; + +/* 用户结构体 */ +typedef struct +{ + char username[VTY_USERNAME_LEN]; + char password[VTY_USERNAME_LEN]; + uint8_t level; /* 用户等级 */ +} vty_user_t; + +/* VTY struct. */ +typedef struct +{ + int fd; /* File descripter of this vty. */ + VTY_TYPE_E type; /* Is this vty connect to file or not */ + uint32_t node; /* Node status of this vty */ + char *address; /* What address is this vty comming from. */ + uint32_t fail_count; /* Failure count */ + + buf_t *out_buf; /* Output buffer. */ + char *buf; /* Command input buffer */ + uint32_t cp; /* Command cursor point */ + uint32_t length; /* Command length */ + uint32_t max; /* Command max length. */ + + char *hist[VTY_MAXHIST]; /* Histry of command */ + uint32_t hp; /* History lookup current point */ + uint32_t hindex; /* History insert end point */ + + void *index; /* For current referencing point of interface, route-map, access-list etc... */ + void *index_sub; /* For multiple level index treatment such as key chain and key. */ + unsigned char escape; /* For escape character. */ + VTY_STAT_E status; /* Current vty status. */ + + /* IAC handling: was the last character received the + IAC (interpret-as-command) escape character (and therefore the next + character will be the command code)? Refer to Telnet RFC 854. */ + unsigned char iac; + unsigned char iac_sb_in_progress; /* IAC SB (option subnegotiation) handling */ + + /* At the moment, we care only about the NAWS (window size) negotiation, + and that requires just a 5-character buffer (RFC 1073): + <16-bit width> <16-bit height> */ + unsigned char sb_buf[TELNET_NAWS_SB_LEN]; + /* How many subnegotiation characters have we received? We just drop + those that do not fit in the buffer. */ + size_t sb_len; + + uint32_t width; /* Window width/height. */ + uint32_t height; + + int32_t lines; /* Configure lines. */ + int32_t monitor; /* Terminal monitor. */ + + int config; /* In configure mode. */ + + /* Read and write thread. */ + thread_t *t_read; + thread_t *t_write; + + /* Timeout seconds and thread. */ + unsigned long v_timeout; + thread_t *t_timeout; + + /* Timeout seconds and thread. */ + vty_user_t user; +} vty_t; + +/* Exported macro ------------------------------------------------------------*/ + +/* Extern global variables ---------------------------------------------------*/ + +/* Extern functions ----------------------------------------------------------*/ +extern void *hash_get(hash_t *hash, void *data, hash_alloc_f alloc_func); + +extern unsigned long thread_consumed_time(RUSAGE_T *now,RUSAGE_T *start, unsigned long *cputime); +extern thread_master_t *thread_master_create(); +extern void thread_call(thread_t *thread); +extern thread_t *thread_add_read(thread_master_t *m, thread_func_f *func, void *arg, int fd, const char* funcname); +extern thread_t *thread_add_write(thread_master_t *m, thread_func_f *func, void *arg, int fd, const char* funcname); +extern thread_t *thread_add_timer(thread_master_t *m, thread_func_f *func, void *arg, long timer, const char* funcname); +extern thread_t *thread_add_timer_msec(thread_master_t *m, thread_func_f *func, void *arg, long timer, const char* funcname); +extern thread_t *thread_add_background(thread_master_t *m, thread_func_f *func, void *arg, long delay, const char *funcname); +extern thread_t *thread_add_event(thread_master_t *m, thread_func_f *func, void *arg, int val, const char* funcname); +extern thread_t *thread_execute(thread_master_t *m, thread_func_f *func, void *arg, int val, const char* funcname); +extern void thread_cancel(thread_t *thread); +extern thread_t *thread_fetch(thread_master_t *m, thread_t *fetch); + +extern int vty_out(vty_t *vty, const char *format, ...); +extern vty_t *vty_create(); +extern int vty_execute(vty_t *vty); +extern int vty_config_lock(vty_t *vty); +extern int vty_config_unlock(vty_t *vty); +extern void vty_question(vty_t *vty, array_t *cmd_line); +extern void vty_print_word(vty_t *vty, char *strs[]); +extern void vty_free_match_strs(char *match_strs[]); +extern void vty_will_echo(vty_t *vty); +extern void vty_will_suppress_go_ahead(vty_t *vty); +extern void vty_dont_linemode(vty_t *vty); +extern void vty_do_window_size(vty_t *vty); +extern void vty_prompt(vty_t *vty); +extern void vty_close(vty_t *vty); +extern void vty_init(void); +extern void vty_event(VTY_EVENT_E event, int sock, vty_t *vty); +extern void vty_log(const char *level, const char *proto_str, const char *format, char *time_str, va_list va); +extern void vty_print(const char *format, va_list va); +extern void vty_serv_sock_family(const char* addr, unsigned short port, int family); +extern void vty_reset(void); +extern void vty_version_print(vty_t *vty); + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/a_process/module.mk b/app/lib/a_process/module.mk new file mode 100644 index 0000000..d9f837b --- /dev/null +++ b/app/lib/a_process/module.mk @@ -0,0 +1,3 @@ +local_src := $(patsubst $(SOURCE_DIR)/%,%,$(wildcard $(SOURCE_DIR)/$(subdirectory)/*.c)) + +$(eval $(call make-library,$(subdirectory)/liba_process.a,$(local_src))) \ No newline at end of file diff --git a/app/lib/a_process/pd_cpld.c b/app/lib/a_process/pd_cpld.c new file mode 100755 index 0000000..f72dd0d --- /dev/null +++ b/app/lib/a_process/pd_cpld.c @@ -0,0 +1,305 @@ +/****************************************************************************** + * file lib/process/pd_cpld.c + * author YuLiang + * version 1.0.0 + * date 31-July-2023 + * brief This file provides all the CPLD operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2023 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ + +/* 用户代码头文件. */ +#include "cmd.h" +#include "hwgpio.h" +#include "pd_cpld.h" + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ +#define CPLD_CS_1 gpio_val_set(cpld_cs_idx, 1) +#define CPLD_CS_0 gpio_val_set(cpld_cs_idx, 0) + +#define CPLD_SCLK_1 gpio_val_set(cpld_sclk_idx, 1) +#define CPLD_SCLK_0 gpio_val_set(cpld_sclk_idx, 0) + +#define CPLD_MOSI_1 gpio_val_set(cpld_mosi_idx, 1) +#define CPLD_MOSI_0 gpio_val_set(cpld_mosi_idx, 0) + +#define CPLD_MISO_READ(_val_) gpio_val_get(cpld_miso_idx, _val_) + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +cpld_version_t cpld_version; +static pthread_mutex_t cpld_mutex; // 多线程同时操作的信号量. +static int32_t cpld_cs_idx; +static int32_t cpld_sclk_idx; +static int32_t cpld_mosi_idx; +static int32_t cpld_miso_idx; + +/* Private function prototypes -----------------------------------------------*/ +/* 读取 CPLD 寄存器. */ +CMD(cpld_reg_read, + cpld_reg_read_cmd, + "cpld read WORD", + "CPLD\n" + "CPLD register read.\n" + "Register address (HH)\n") +{ + return CMD_SUCCESS; + uint16_t reg = 0; + uint16_t temp = 0; + char* str = NULL; + + reg = strtol(argv[0], &str, 16); + if (*str != '\0') + { + return CMD_ERR_NO_MATCH; + } + + cpld_read(reg, 1, &temp); + vty_out(vty, "Read reg 0x%02x value: 0x%04x%s\n", reg, temp, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* 写 CPLD 寄存器. */ +CMD(cpld_reg_write, + cpld_reg_write_cmd, + "cpld write WORD WORD", + "CPLD\n" + "CPLD register write\n" + "Register address (HH)\n" + "Value (HHHH)\n") +{ + return CMD_SUCCESS; + uint16_t reg = 0; + uint16_t temp = 0; + char* str = NULL; + + reg = strtol(argv[0], &str, 16); + if (*str != '\0') + { + return CMD_ERR_NO_MATCH; + } + + temp = strtol(argv[1], &str, 16); + if (*str != '\0') + { + return CMD_ERR_NO_MATCH; + } + + cpld_write(reg, 1, &temp); + vty_out(vty, "Write reg 0x%02x value: 0x%04x%s", reg, temp, VTY_NEWLINE); + cpld_read(reg, 1, &temp); + vty_out(vty, "Read reg 0x%02x value: 0x%04x%s\n", reg, temp, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* 显示 CPLD 信息. */ +CMD(show_cpld, + show_cpld_cmd, + "show cpld", + SHOW_STR + "CPLD\n") +{ + return CMD_SUCCESS; + vty_out(vty, "Version: %04x-%02x-%02x%s\n", cpld_version.year, cpld_version.month, cpld_version.day, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +//SPI可以同时读取和写入数据,因此一个函数即可满足要求 +int32_t _cpld_half_word_write(uint16_t data) +{ + uint8_t i = 0; + + for(i = 0; i < 16; i++) + { + if(data & 0x8000) + { + CPLD_MOSI_1; + } + else + { + CPLD_MOSI_0; + } + data <<= 1; + + CPLD_SCLK_1; + CPLD_SCLK_0; + + } + + return E_NONE; +} + +//SPI可以同时读取和写入数据,因此一个函数即可满足要求 +int32_t _cpld_half_word_read(uint16_t *data) +{ + uint8_t i = 0; + uint8_t gpio_val = 0; + uint16_t data_rx = 0; + + for(i = 0; i < 16; i++) + { + CPLD_SCLK_1; + + data_rx <<= 1; + CPLD_MISO_READ(&gpio_val); + if (gpio_val) + { + data_rx |= 0x01; + } + + CPLD_SCLK_0; + } + + *data = data_rx; + return E_NONE; +} + +/* Internal functions --------------------------------------------------------*/ + + + +/* Interface functions -------------------------------------------------------*/ +/* CPLD 模块初始化代码. */ +int32_t cpld_handle_init(void) +{ + uint16_t temp = 0; + +#if 0 + /* 申请信号量, 防止多个线程同时操作 fifo. */ + if (pthread_mutex_init(&cpld_mutex, NULL) != 0) + { + DBG(DBG_M_FIFO_ERR, "ERROR at mutex init return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + /* 申请 GPIO. */ + cpld_cs_idx = gpio_export(GPIO_CPLD_CS); + if (cpld_cs_idx < 0) + { + DBG(DBG_M_PD_CPLD, "ERROR return %d!\r\n", cpld_cs_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_PD_CPLD, gpio_dir_set(cpld_cs_idx, GPIO_DIR_OUT)); + + cpld_sclk_idx = gpio_export(GPIO_CPLD_SCLK); + if (cpld_sclk_idx < 0) + { + DBG(DBG_M_PD_CPLD, "ERROR return %d!\r\n", cpld_sclk_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_PD_CPLD, gpio_dir_set(cpld_sclk_idx, GPIO_DIR_OUT)); + + cpld_mosi_idx = gpio_export(GPIO_CPLD_MOSI); + if (cpld_mosi_idx < 0) + { + DBG(DBG_M_PD_CPLD, "ERROR return %d!\r\n", cpld_mosi_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_PD_CPLD, gpio_dir_set(cpld_mosi_idx, GPIO_DIR_OUT)); + + cpld_miso_idx = gpio_export(GPIO_CPLD_MISO); + if (cpld_miso_idx < 0) + { + DBG(DBG_M_PD_CPLD, "ERROR return %d!\r\n", cpld_miso_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_PD_CPLD, gpio_dir_set(cpld_miso_idx, GPIO_DIR_IN)); + + CPLD_CS_1; + CPLD_SCLK_0; + + /* 获取版本号. */ + cpld_read(CPLD_REG_V0, 1, &temp); + cpld_version.year = temp; + cpld_read(CPLD_REG_V1, 1, &temp); + cpld_version.month = temp >> 8; + cpld_version.day = temp & 0x00ff; +#endif + + /* 注册命令. */ + cmd_install_element(COMMON_NODE, &show_cpld_cmd); + cmd_install_element(ENABLE_NODE, &cpld_reg_read_cmd); + cmd_install_element(ENABLE_NODE, &cpld_reg_write_cmd); + + return E_NONE; +} + +int32_t cpld_read(uint16_t addr, uint16_t len, uint16_t *data) +{ + int32_t i = 0; + + pthread_mutex_lock(&cpld_mutex); + CPLD_CS_0; + + _cpld_half_word_write(0x8000 | addr); + + for(i = 0; i < len; i++) + { + _cpld_half_word_read(data + i); + } + + CPLD_CS_1; + pthread_mutex_unlock(&cpld_mutex); + + return E_NONE; +} + +int32_t cpld_write(uint16_t addr, uint16_t len, uint16_t *data) +{ + int32_t i = 0; + + pthread_mutex_lock(&cpld_mutex); + CPLD_CS_0; + + _cpld_half_word_write(0x7fff & addr); + + for(i = 0; i < len; i++) + { + _cpld_half_word_write(data[i]); + } + + CPLD_CS_1; + pthread_mutex_unlock(&cpld_mutex); + + return E_NONE; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/a_process/pd_csg.c b/app/lib/a_process/pd_csg.c new file mode 100755 index 0000000..75daaf0 --- /dev/null +++ b/app/lib/a_process/pd_csg.c @@ -0,0 +1,1159 @@ +/****************************************************************************** + * file lib/process/pd_csg.c + * author YuLiang + * version 1.0.0 + * date 27-Feb-2023 + * brief This file provides all the csg server operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef CFG_DEV_TYPE_LAND_PD +/* 标准C库头文件. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 用户代码头文件. */ +#include "cmd.h" +#include "fifo.h" +#include "pd_main.h" +#include "pd_dau.h" +#include "pd_csg.h" + +/* Private define ------------------------------------------------------------*/ +#define CSG_LOCK pthread_mutex_lock(&csg.mutex) +#define CSG_UNLOCK pthread_mutex_unlock(&csg.mutex) + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +csg_t csg; + +/* Private function prototypes -----------------------------------------------*/ +extern void _csg_server_set(int32_t ip, uint16_t port); + +/* Internal functions --------------------------------------------------------*/ +CMD(csg_server_set, + csg_server_set_cmd, + "csg server A.B.C.D <1-65535>", + "Csg\n" + "Server\n" + "IPv4 address\n" + "UDP port\n") +{ + return CMD_SUCCESS; + _csg_server_set(inet_addr((char*)argv[0]), strtol((char*)argv[1], NULL, 10)); + + return CMD_SUCCESS; +} + +void _csg_server_set(int32_t ip, uint16_t port) +{ + /* 比较配置 */ + if (csg.server_ip != ip + || csg.server_port != port) + { + csg.server_ip = ip; + csg.server_port = port; + + /* 发送数据. */ + bzero(&csg.server, sizeof(csg.server)); + csg.server.sin_family = AF_INET; + csg.server.sin_addr.s_addr = csg.server_ip; + csg.server.sin_port = htons(csg.server_port); + } +} + +/* 配置保存函数. */ +int _csg_config_save(vty_t* vty) +{ + int16_t i = 0; + struct in_addr addr; + + addr.s_addr = csg.server_ip; + vty_out(vty, "csg server %s %d%s", inet_ntoa(addr), csg.server_port, VTY_NEWLINE); + i++; + + return i; +} + +/* 校验收到包的包头, 长度, 校验码. */ +int32_t _csg_pkt_check(char *pkt, int32_t len) +{ + csg_pkt_head_t *head = (csg_pkt_head_t*)pkt; + csg_pkt_tail_t *tail = (csg_pkt_tail_t*)(pkt + len - CSG_TAIL_LEN); + uint16_t checksum = 0; + uint16_t i = 0; + + if (head->head != 0x55aa) + { + DBG(DBG_M_PD_CSG_ERR, "Pkt head %x error!\r\n", head->head); + return E_ERROR; + } + if( tail->tail != 0x5a5a) + { + DBG(DBG_M_PD_CSG_ERR, "Pkt tail %x error!\r\n", tail->tail); + return E_ERROR; + } + + if (head->len != (len - CSG_TOTLE_LEN)) + { + DBG(DBG_M_PD_CSG_ERR, "Pkt len %d(%d) error!\r\n", len - CSG_TOTLE_LEN, head->len); + return E_ERROR; + } + + for (i = CSG_HEAD_LEN; i < len - CSG_TAIL_LEN; i++) + { + checksum += (uint8_t)pkt[i]; + } + if (checksum != tail->checksum) + { + DBG(DBG_M_PD_CSG_ERR, "Pkt checksum %x(%x) error!\r\n", checksum, tail->checksum); + return E_ERROR; + } + + return E_NONE; +} + +/* 包头填充. */ +void _csg_head_init(char *buf, uint16_t cmd, uint16_t len) +{ + csg_pkt_head_t *head = (csg_pkt_head_t*)buf; + + memset(buf, 0, len + CSG_HEAD_LEN); + head->head = 0x55AA; + head->cmd = cmd; + snprintf((char*)head->dev_num, PD_DEV_NUM_LEN, "%s", host.name); + head->len = len; +} + +void _csg_cheaksum(char *buf, uint16_t len) +{ + char *data = buf + CSG_HEAD_LEN; + csg_pkt_tail_t *tail = (csg_pkt_tail_t*)(data + len); + uint16_t i = 0; + uint16_t checksum = 0; + + for(i = 0; i < len; i++) + { + checksum += (uint8_t)data[i]; + } + + tail->checksum = checksum; + tail->tail = 0x5a5a; +} + +/* 主动连接请求. */ +int32_t _csg_connect_send(void) +{ + char *pkt = csg.buf_reply; + uint16_t *data = (uint16_t*)(pkt + CSG_HEAD_LEN); + int32_t rv = 0; + uint16_t len = 8; + + _csg_head_init(pkt, CSG_CONNECT_REQ, len); + + *data = sofrware_version_get(); + _csg_cheaksum(pkt, len); + + rv = sendto(csg.fd, pkt, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 心跳包. */ +int32_t _csg_heartbeat_send(void) +{ + char *pkt = csg.buf_reply; + csg_pkt_heartbeat_t *data = (csg_pkt_heartbeat_t*)(pkt + CSG_HEAD_LEN); + int32_t rv = 0; + uint16_t len = sizeof(csg_pkt_heartbeat_t); + uint16_t i = 0; + + _csg_head_init(pkt, CSG_HEARTBEAT_REQ, len); + + data->heartbeat = csg.heartbeat; + data->utc = time(NULL) - CSG_SHIQUCHA; + data->freq = 50; + + for(i = 0; i < PD_DAU_SUM; i++) + { + if (dau[i]) + { + data->dau_state[i] = dau[i]->is_connect; + } + else + { + data->dau_state[i] = 0; + } + } + + data->out_sync = 0; + data->pt_sync = 0; + data->in_sync = 0; + if (dau[0]) + { + if (pd_state.sync) + { + if (PD_SYNC_PT == pd_config.config.sync_mode) + { + data->pt_sync = 1; + } + else if (PD_SYNC_POWER == pd_config.config.sync_mode) + { + data->in_sync = 1; + } + else if (PD_SYNC_OUTSIDE == pd_config.config.sync_mode) + { + data->out_sync = 1; + } + } + } + + + for(i = 0; i < PD_PORT_SUM; i++) + { + data->port_link_alarm[i] = 0; + } + + _csg_cheaksum(pkt, len); + + rv = sendto(csg.fd, pkt, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + csg.heartbeat++; + if (csg.heartbeat > 3) + { + csg.is_connect = FALSE; + } + + return E_NONE; +} + +//反馈设置状态的公共函数 2Byte +int32_t _csg_set_state_send(char *pkt, uint16_t cmd, uint8_t vport, uint16_t state) +{ + uint16_t *data = (uint16_t*)(pkt + CSG_HEAD_LEN); + int32_t rv = 0; + uint16_t len = 4; + + _csg_head_init(pkt, cmd, len); + + *data = state; + data++; + *data = vport; + + _csg_cheaksum(pkt, len); + + rv = sendto(csg.fd, pkt, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 主动提交实时图谱. */ +int32_t _csg_real_image_send(pd_prps_t *data) +{ + char *buf = csg.buf_send; + csg_real_image_t *head = (csg_real_image_t*)(buf + CSG_HEAD_LEN); + pd_prps_data_t *data_port = NULL; + int32_t rv = 0; + uint16_t len = sizeof(csg_real_image_t) + 1280; + uint8_t i = 0; + uint8_t unit = 0; + uint8_t port = 0; + + _csg_head_init(buf, CSG_REAL_IMAGE_ACK, len); + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + for(port = 0; port < dau[unit]->port_num; port++) + { + if (!pd_config.port_config[unit][port].is_concern + || !data->data[unit][port].is_valid) + { + continue; + } + + data_port = &data->data[unit][port]; + head->index = csg.pkt_index++; + head->vport = dau_port_to_vport(unit, port); + head->pkt_sum = 10; + head->max = data_port->max; + head->avg = data_port->avg; + head->cnt = data_port->cnt; + head->is_sub = 0; + head->interval = 0; + head->utc = time(NULL) - CSG_SHIQUCHA - CSG_SHIQUCHA; + + for (i = 0; i < 10; i++) + { + head->pkt_index = i; + memcpy(buf + CSG_HEAD_LEN + sizeof(csg_real_image_t), data_port->data + 640 * i, 1280); + _csg_cheaksum(buf, len); + + rv = sendto(csg.fd, buf, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + } + } + } + } + + return E_NONE; +} + +/* 趋势数据发送. */ +int32_t _csg_trend_send(pd_trend_t *data) +{ + char *buf = csg.buf_send; + csg_trend_head_t *head = (csg_trend_head_t*)(buf + CSG_HEAD_LEN); + csg_trend_t *trend = NULL; + pd_trend_data_t *data_port = NULL; + int32_t rv = 0; + int32_t vport = 0; + uint16_t len = sizeof(csg_trend_head_t); + uint8_t unit = 0; + uint8_t port = 0; + + _csg_head_init(buf, CSG_TREND_ACK, len); + + head->idx = data->index; + head->is_sub = 0; + head->interval = pd_config.config.trend_period; + head->utc = data->utc - CSG_SHIQUCHA - CSG_SHIQUCHA; + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + for(port = 0; port < dau[unit]->port_num; port++) + { + vport = dau_port_to_vport(unit, port); + if (vport <= 0) + { + continue; + } + + trend = &head->data[vport - 1]; + data_port = &data->data[unit][port]; + + trend->is_valid = 1; + trend->max = data_port->max; + trend->avg = data_port->avg; + trend->noise = data_port->noise; + trend->cnt = data_port->cnt; + trend->phase = data_port->phase; + trend->event_cnt = data_port->event_cnt; + } + } + + _csg_cheaksum(buf, len); + rv = sendto(csg.fd, buf, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_ERROR; + } + + return E_NONE; +} + +//按通道主动提交事件数据 +int32_t _csg_event_send(pd_event_t *data) +{ + char *buf = csg.buf_send; + csg_event_head_t *head = (csg_event_head_t*)(buf + CSG_HEAD_LEN); + int32_t rv = 0; + uint16_t len = sizeof(csg_event_head_t) + 1280; + uint8_t i = 0; + + _csg_head_init(buf, CSG_EVENT_REQ, len); + + head->idx = csg.pkt_index++; + head->vport = data->vport; + head->pkt_sum = 10; + head->type = data->type; + head->max = data->max; + head->level = data->level; + head->pd_Type = data->type; + for(i = 0;i < 8; i++) + { + head->type_per[i] = data->type_per[i]; + } + head->PositionDistance = data->PositionDistance; + head->utc = data->utc - CSG_SHIQUCHA - CSG_SHIQUCHA; + head->event_idx = data->index; + head->cnt = data->cnt; + + for (i = 0; i < 10; i++) + { + head->pkt_idx = i; + memcpy(buf + CSG_HEAD_LEN + sizeof(csg_event_head_t), data->data + 640 * i, 1280); + _csg_cheaksum(buf, len); + + rv = sendto(csg.fd, buf, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + } + } + + return E_NONE; +} + +void _csg_connect_recv(void) +{ + csg.is_connect = TRUE; + csg.heartbeat = 0; +} + +/* 解析心跳报文. */ +void _csg_heartbeat_recv(char *pkt) +{ + csg.heartbeat = 0; + uint32_t server_time = *(uint32_t*)(pkt + CSG_HEAD_LEN); + + server_time += CSG_SHIQUCHA; + if (abs(server_time - time(NULL)) > 10) + { + time_set(server_time + 28800); //北京时间 + } +} + +/* 主动连接请求. */ +int32_t _csg_acitve_connect_recv(char *pkt) +{ + uint16_t *data = (uint16_t*)(pkt + CSG_HEAD_LEN); + int32_t rv = 0; + uint16_t len = 8; + + _csg_head_init(pkt, CSG_CONNECT_REQ, len); + + *data = sofrware_version_get(); + _csg_cheaksum(pkt, len); + + rv = sendto(csg.fd, pkt, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + csg.is_connect = TRUE; + csg.heartbeat = 0; + + return E_NONE; +} + +/* 设备重启报文. */ +void _csg_reboot_recv(char *pkt) +{ + reboot_system(DBG_M_PD_CSG, BOOT_REMOTE_RESET); +} + +/* 厂家参数设置报文处理. + 说明:修改本地ip 设备ID 服务器地址时需要重启。 +*/ +int32_t _csg_factory_set_recv(char *pkt) +{ + struct sockaddr_in server; + pd_factory_old_t *factory = &pd_config.factory; + uint8_t unit = 0; + uint8_t port = 0; + int32_t vport = 0; + int need_save = 0; + int change_ip = 0; + BOOT_MSG boottype = BOOT_NONE; + + memcpy(factory, pkt + CSG_HEAD_LEN, sizeof(pd_factory_old_t) - 24); + + if (strncmp((char *)(factory->dev_num), host.name, sizeof(host.name))) + { + snprintf((char*)host.name, PD_DEV_NUM_LEN, "%s", factory->dev_num); + need_save++; + boottype = BOOT_REMOTE_HOST_NAME_CHANGE; + } + + if (0 == strncmp((char*)factory->dev_type, "GOM5010", 7)) + { + device_info.id_major = 5010; + } + device_info.factory_date = factory->factory_date; + device_info.deployment_date = factory->deployment_date; + server.sin_addr.s_addr = factory->ipv4; + if (strncmp(device_info.host_ip, inet_ntoa(server.sin_addr), sizeof(device_info.host_ip))) + { + snprintf((char*)device_info.host_ip, INET_ADDRSTRLEN, "%s", inet_ntoa(server.sin_addr)); + need_save++; + change_ip++; + boottype = BOOT_REMOTE_IP_CHANGE; + } + + memcpy(device_info.mac, factory->mac, MAC_ADDR_LEN); + + if (csg.server_ip != factory->server_ipv4) + { + csg.server_ip = factory->server_ipv4; + need_save++; + boottype = BOOT_REMOTE_SERVER_IP_CHANGE; + } + + if (csg.server_port != factory->server_port) + { + csg.server_port = factory->server_port; + need_save++; + boottype = BOOT_REMOTE_SERVER_PORT_CHANGE; + } + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + for(port = 0; port < dau[unit]->port_num; port++) + { + vport = dau_port_to_vport(unit, port); + if (vport <= 0) + { + continue; + } + + dau_port_type_set(unit, port, factory->port_type[vport - 1]); + + dau_reg_port_write(unit, port); + + need_save++; // save file + } + } + + _csg_set_state_send(pkt, CSG_FACTORY_SET_ACK, 0, 1); + + if (need_save) + { + vtysh_config_save(); + if (change_ip) + { + uint8_t mac[MAC_ADDR_LEN] = {0}; + mac_generate_from_ip(device_info.host_ip, mac); + memcpy(device_info.mac, mac, MAC_ADDR_LEN); + vtysh_device_save(); + vtysh_eth0_save(); + } + } + if (boottype) + { + reboot_system(DBG_M_PD_CSG, boottype); + } + return 0; +} + +/* 厂家参数查询报文处理. */ +int32_t _csg_factory_get_recv(char *pkt) +{ + pd_factory_old_t *factory = &pd_config.factory; + uint16_t len = sizeof(pd_factory_old_t); + uint8_t unit = 0; + uint8_t port = 0; + int32_t vport = 0; + int32_t rv = 0; + + _csg_head_init(pkt, CSG_FACTORY_GET_ACK, len); + + snprintf((char*)factory->dev_num, PD_DEV_NUM_LEN, "%s", host.name); + snprintf((char*)factory->dev_type, PD_DEV_TYPE_LEN, "GOM%d", device_info.id_major); + factory->factory_date = device_info.factory_date; + factory->deployment_date = device_info.deployment_date; + factory->ipv4 = inet_addr((char*)device_info.host_ip); + memcpy(factory->mac, device_info.mac, MAC_ADDR_LEN); + factory->server_ipv4 = csg.server_ip; + factory->server_port = csg.server_port; + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + for(port = 0; port < dau[unit]->port_num; port++) + { + vport = dau_port_to_vport(unit, port); + if (vport <= 0) + { + continue; + } + + factory->port_type[vport - 1] = (dau[unit]->port_reg[port].CR & DAU_CR_PT_Msk) >> DAU_CR_PT_Pos; + } + } + + memcpy(pkt + CSG_HEAD_LEN, factory, len); + + _csg_cheaksum(pkt, len); + + rv = sendto(csg.fd, pkt, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 配置用户参数报文报文处理. */ +int32_t _csg_config_set_recv(char *pkt) +{ + int need_save = 0; + int cpld_save = 0; + pd_config_old_t *pnet = (pd_config_old_t *)(pkt + CSG_HEAD_LEN); + + if ((pd_config.config.sync_mode != pnet->sync_mode) + || (pd_config.config.real_period != pnet->real_period) + || (pd_config.config.trend_period != pnet->trend_period) + || (pd_config.config.heartbeat_period != pnet->heartbeat_period)) + { + need_save++; + } + if (pd_config.config.sync_mode != pnet->sync_mode) + { + cpld_save++; + } + + memcpy(&pd_config.config, pkt + CSG_HEAD_LEN, sizeof(pd_config_old_t)); + + if (need_save) + { + vtysh_config_save(); + } + + if (cpld_save) + { + pd_sync_mode_set(); + } + + return _csg_set_state_send(pkt, CSG_CONFIG_SET_ACK, 0, 1); +} + +/* 查询用户参数查询报文处理. */ +int32_t _csg_config_get_recv(char *pkt) +{ + uint16_t len = sizeof(pd_config_old_t); + int32_t rv = 0; + + _csg_head_init(pkt, CSG_CONFIG_GET_ACK, len); + + memcpy(pkt + CSG_HEAD_LEN, &pd_config.config, len); + + _csg_cheaksum(pkt, len); + + rv = sendto(csg.fd, pkt, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 通道提交端口参数设置. */ +int32_t _csg_port_config_set_recv(char *pkt) +{ + uint8_t vport = *(uint8_t*)(pkt + CSG_HEAD_LEN); + uint8_t unit = 0; + uint8_t port = 0; + + if (dau_vport_to_port(vport, &unit, &port) != E_NONE) + { + DBG(DBG_M_PD_CSG_ERR, "Pkt port %d error!\r\n", vport); + return _csg_set_state_send(pkt, CSG_PORT_CONFIG_SET_ACK, vport, 0); + } + + memcpy(&pd_config.port_config[unit][port].config, pkt + CSG_HEAD_LEN, sizeof(pd_port_config_old_t)); + pd_config.port_config[unit][port].filter_cfg = pd_config.port_config[unit][port].config.filter; + dau_port_config_set(unit, port); + dau_reg_port_write(unit, port); + + vtysh_config_save(); + + return _csg_set_state_send(pkt, CSG_PORT_CONFIG_SET_ACK, vport, 1); +} + +/* 按通道提交端口参数查询结果. */ +int32_t _csg_port_config_get_recv(char *pkt) +{ + uint8_t vport = *(uint8_t*)(pkt + CSG_HEAD_LEN); + uint16_t len = sizeof(pd_port_config_old_t); + uint8_t unit = 0; + uint8_t port = 0; + int32_t rv = 0; + + if (dau_vport_to_port(vport, &unit, &port) != E_NONE) + { + DBG(DBG_M_PD_CSG_ERR, "Pkt port %d error!\r\n", vport); + return E_ERROR; + } + + _csg_head_init(pkt, CSG_PORT_CONFIG_GET_ACK, len); + + memcpy(pkt + CSG_HEAD_LEN, &pd_config.port_config[unit][port].config, len); + + _csg_cheaksum(pkt, len); + + rv = sendto(csg.fd, pkt, len + CSG_TOTLE_LEN, 0, (struct sockaddr*)&csg.server, sizeof(csg.server)); + if (rv < 0) + { + DBG(DBG_M_PD_CSG_ERR, "Sendto return %s!\r\n", safe_strerror(errno)); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 召唤趋势数据. */ +int32_t _csg_trend_get_recv(char *pkt) +{ +#if 0 + uint32_t time_start = *(uint32_t*)pkt; + uint32_t time_end = *(uint32_t*)(pkt+4); + struct tm *lt = localtime(&time_start); + + char basePath[256] = {0}; + sprintf(basePath,"%s/%s/%d/%02d/",fixedHead,"Trend",lt->tm_year+1900,lt->tm_mon+1); + + img_num=0; + if(!readFileList(basePath))LOG_ERR_RETURN + + int i = 0,fLen = 0; + for(i = 0;i < img_num;i ++) + { + int fileUtc = getUctByFileName(img_path[i]); + if(fileUtc >= startTime && fileUtc <= stopTime) + { + U8 *pFile = NULL; + fLen = ReadHexFile(img_path[i],&pFile); + if(fLen < sizeof(strTrendData))continue; + csgAutoPostHufTrend((strTrendData *)pFile,true); + } + } +#endif + + return E_NONE; +} + +/* 解析HUF实时图谱召唤报文. */ +void _csg_real_image_recv(char *pkt) +{ + csg_real_image_get_t *data = (csg_real_image_get_t*)(pkt + CSG_HEAD_LEN); + uint8_t unit = 0; + uint8_t port = 0; + + if (dau_vport_to_port(data->vport, &unit, &port) != E_NONE) + { + DBG(DBG_M_PD_CSG_ERR, "Pkt port %d error!\r\n", data->vport); + return; + } + + pd_config.port_config[unit][port].r_noise_reduction = data->r_noise_reduction; + pd_config.port_config[unit][port].auto_noise_reduction = data->auto_noise_reduction; + pd_config.port_config[unit][port].manual_noise_reduction = data->manual_noise_reduction * 10; + pd_config.port_config[unit][port].is_concern = data->is_concern; + + if (data->is_concern) + { + BITMAP_SET(pd_config.concern_bitmap, data->vport - 1); + if ((data->filter > 0 && data->filter < 5) + && data->filter != pd_config.port_config[unit][port].filter_cfg) + { + pd_config.port_config[unit][port].filter_cfg = data->filter; + dau_port_config_set(unit, port); + dau_reg_port_write(unit, port); + } + } + else + { + BITMAP_RESET(pd_config.concern_bitmap, data->vport - 1); + if (pd_config.port_config[unit][port].filter_cfg != pd_config.port_config[unit][port].config.filter) + { + pd_config.port_config[unit][port].filter_cfg = pd_config.port_config[unit][port].config.filter; + dau_port_config_set(unit, port); + dau_reg_port_write(unit, port); + } + } +} + +/* DAU 报文发送处理. */ +int32_t _csg_recv_process(char *pkt, uint32_t len) +{ + csg_pkt_head_t *head = (csg_pkt_head_t*)pkt; + + /* 报文头和 CRC 校验. */ + LD_E_RETURN(DBG_M_PD_CSG_ERR, _csg_pkt_check(pkt, len)); + + /* 共有命令处理. */ + switch (head->cmd) + { + case CSG_CONNECT_ACK: + _csg_connect_recv(); + break; + case CSG_HEARTBEAT_ACK: + _csg_heartbeat_recv(pkt); + break; + case CSG_ACITVE_CONNECT_REQ: + _csg_acitve_connect_recv(pkt); + break; + case CSG_REBOOT_REQ: + _csg_reboot_recv(pkt); + break; + case CSG_FACTORY_SET_REQ: + _csg_factory_set_recv(pkt); + break; + case CSG_FACTORY_GET_REQ: + _csg_factory_get_recv(pkt); + break; + case CSG_CONFIG_SET_REQ: + _csg_config_set_recv(pkt); + break; + case CSG_CONFIG_GET_REQ: + _csg_config_get_recv(pkt); + break; + case CSG_PORT_CONFIG_SET_REQ: + _csg_port_config_set_recv(pkt); + break; + case CSG_PORT_CONFIG_GET_REQ: + _csg_port_config_get_recv(pkt); + break; + case CSG_TREND_REQ: + _csg_trend_get_recv(pkt); + break; + case CSG_REAL_IMAGE_REQ: + _csg_real_image_recv(pkt); + break; + default: + //DBG(DBG_M_PD_CSG_ERR, "Cmd %d error!\r\n", head->cmd); + break; + } + + csg.communication_time = time(NULL); + return E_NONE; +} + +/* 心跳和连接处理函数. */ +void *_csg_recv_handle(void *arg) +{ + struct sockaddr_in server; + socklen_t server_len; + int32_t addr = 0; + uint16_t data_len = 0; + + while (!is_system_init) + { + sleep(1); + } + + while(1) + { + /* 读取数据. */ + data_len = recvfrom(csg.fd, csg.buf_recv, CSG_PKT_LEN, 0, (struct sockaddr*)&server, &server_len); + if (data_len <= 0) + { + DBG(DBG_M_PD_CSG_ERR, "Recvfrom return ERROR %s!\r\n", safe_strerror(errno)); + continue; + } + + addr = server.sin_addr.s_addr; + if (addr != csg.server_ip) + { + continue; + } + + _csg_recv_process(csg.buf_recv, data_len); + } + + return NULL; +} + +/* 向后台服务器发送报文处理函数. */ +void *_csg_send_handle(void *arg) +{ + pd_csg_msg_t *recv_msg = NULL; + + while(1) + { + if (fifo_read(csg.data_fifo_id, (void**)&recv_msg) != 0) + { + DBG(DBG_M_PD_CSG_ERR, "ERROR at fifo %d read!\r\n", csg.data_fifo_id); + continue; + } + + if (csg.is_connect) + { + /* 数据处理. */ + switch(recv_msg->type) + { + case PD_SEND_TYPE_PRPS: + _csg_real_image_send((pd_prps_t*)recv_msg->data); + break; + case PD_SEND_TYPE_TREND: + _csg_trend_send((pd_trend_t*)recv_msg->data); + break; + case PD_SEND_TYPE_EVENT: + _csg_event_send((pd_event_t*)recv_msg->data); + break; + default: + break; + } + } + + /* 释放数据内存, 注意一定要在 fifo_push 之前调用, 因为 fifo_push 后 recv_msg 已被释放. */ + XFREE(MTYPE_CSG, recv_msg->data); + fifo_push(csg.data_fifo_id); + + csg.communication_time = time(NULL); + } + + return NULL; +} + +/* 心跳和连接处理函数. */ +void *_csg_heartbeat_handle(void *arg) +{ + time_t now = 0; + time_t t_connect = 0; + time_t t_heartbeat = 0; + + while (!is_system_init) + { + sleep(1); + } + + while(1) + { + sleep(1); + now = time(NULL); + + /* 发送连接报文. */ + if (!csg.is_connect) + { + if (now - t_connect >= 10) + { + _csg_connect_send(); + t_connect = now; + } + continue; + } + + /* 发送心跳包. */ + if ((now - t_heartbeat) / 60 >= pd_config.config.heartbeat_period) + { + _csg_heartbeat_send(); + t_heartbeat = now; + } + } + + return NULL; +} + +/* 后台通讯公共部分初始化. */ +int32_t _csg_handle_init_common(void) +{ + struct sockaddr_in server; + struct sched_param param; + pthread_attr_t attr; + pthread_t pid; + int32_t rv = 0; + int fd = 0; + + /* 创建协议 socket. */ + if (0 == csg.fd) + { + /* 创建socket */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + log_err(LOG_CSG, "ERROR at socket create return %s!", safe_strerror(errno)); + return E_SYS_CALL; + } + + /* 绑定端口 */ + bzero(&server, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = htonl(INADDR_ANY); + server.sin_port = htons(7777); + if(bind(fd, (struct sockaddr*)&server, sizeof(server)) < 0) + { + log_err(LOG_CSG, "ERROR at socket bind return %s!", safe_strerror(errno)); + close(fd); + return E_SYS_CALL; + } + + /* 发送数据. */ + csg.server_ip = inet_addr("192.168.1.30"); + csg.server_port = 10000; + bzero(&csg.server, sizeof(csg.server)); + csg.server.sin_family = AF_INET; + csg.server.sin_addr.s_addr = csg.server_ip; + csg.server.sin_port = htons(csg.server_port); + + /* 保存数据. */ + csg.fd = fd; + } + + csg.data_fifo_id = fifo_create(CSG_DATA_FIFO, 128); + if (csg.data_fifo_id < 0) + { + log_err(LOG_CSG, "Open fifo " CSG_DATA_FIFO " error!"); + return E_ERROR; + } + + pthread_mutex_init(&csg.mutex, NULL); + + /* 配置线程RR调度, 优先级70 */ + pthread_attr_init(&attr); + param.sched_priority = 70; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _csg_send_handle, NULL); + if (rv != 0) + { + log_err(LOG_CSG, "PD can't create pthread %d!", rv); + return E_SYS_CALL; + } + else + { + thread_m_add("CSG_SEND", pid); + } + pthread_attr_destroy(&attr); + + /* 配置线程RR调度, 优先级70 */ + pthread_attr_init(&attr); + param.sched_priority = 70; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _csg_heartbeat_handle, NULL); + if (rv != 0) + { + log_err(LOG_CSG, "PD can't create heartbeat pthread %d!", rv); + return E_SYS_CALL; + } + else + { + thread_m_add("CSG_HEARTBEAT", pid); + } + pthread_attr_destroy(&attr); + + /* 配置线程RR调度, 优先级70 */ + pthread_attr_init(&attr); + param.sched_priority = 70; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _csg_recv_handle, NULL); + if (rv != 0) + { + log_err(LOG_CSG, "PD can't create recv pthread %d!", rv); + return E_SYS_CALL; + } + else + { + thread_m_add("CSG_RCVE", pid); + } + pthread_attr_destroy(&attr); + + return E_NONE; +} + +/* Interface functions -------------------------------------------------------*/ +/* 后台通讯模块初始化. */ +int32_t csg_handle_init(void) +{ + int32_t rv = 0; + +#if 0 + /* 初始化模块. */ + LD_E_RETURN(DBG_M_PD_CSG_ERR, _csg_handle_init_common()); +#endif + + cmd_install_element(CONFIG_NODE, &csg_server_set_cmd); + +#if 0 + /* 注册配置保存函数 */ + rv = cmd_config_node_config_register(CONFIG_PRI_CSG, _csg_config_save); + if (rv != E_NONE) + { + log_err(LOG_CSG, "Command save register ERROR %d!", rv); + return rv; + } +#endif + + return E_NONE; +} +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/a_process/pd_dau.c b/app/lib/a_process/pd_dau.c new file mode 100755 index 0000000..381e2fd --- /dev/null +++ b/app/lib/a_process/pd_dau.c @@ -0,0 +1,2758 @@ +/***************************************************************************** + * file lib/process/pd_dau.c + * author YuLiang + * version 1.0.0 + * date 03-Feb-2023 + * brief This file provides all the dau related operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef CFG_DEV_TYPE_LAND_PD +/* 标准C库头文件. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vty.h" +#include "cmd.h" +#include "fifo.h" +#include "mtimer.h" +#include "hwgpio.h" + +#include "process.h" +#include "pd_main.h" +#include "pd_csg.h" +#include "pd_dau.h" +#include "pd_storage.h" +#include "pd_cpld.h" +#include "pd_dbg.h" + +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ +//#define DAU_IPC_FIFO + +#define DAU_ERR_MAX 5 // DAU 连续发送失败次数. +#define DAU_SEND_ERR_MAX 3 // DAU 发送重发次数. +#define DAU_SEND_TIMEOUT 100000000 // DAU 发送报文超时时间 100ms = 100000000ns. +#define DAU_SEND_TIMEOUT_U 3 // DAU 发送报文超时时间 3s. +#define DAU_ADC_RATE 50 // DAU ADC 采样速率 50MHz. +#define DAU_RECV_FIFO "DAU_RECV_FIFO" + +/* Private macro -------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +dau_ctrl_t dau_ctrl; +dau_t *dau[PD_DAU_SUM]; + +/* DAU 端口类型分类. */ +static const char *dau_port_type_str[PD_PORT_TYPE_COUNT] = +{ + "", + "uhf", + "ae", + "tev", + "hf" +}; + +/* DAU 工作模式. */ +static const char *dau_mode_str[PD_DAU_MODE_COUNT] = +{ + "auto", + "master", + "slave" +}; + +extern dau_port_func_t gis_func; + +/* Private function prototypes -----------------------------------------------*/ +extern int32_t _dau_add(uint8_t unit, uint8_t port_num); +extern void _dau_del(uint8_t unit); +extern void *_dau_send_handle(void *arg); +extern void vtycmd_send(char *pbuf); + +/* Internal functions --------------------------------------------------------*/ +/* DAU 模块数量设置命令. */ +CMD(dau_add, + dau_add_cmd, + "dau <1-2> port-num <1-8>", + "DAU\n" + "Unit id\n" + "Port\n" + "Port number\n") +{ + return CMD_SUCCESS; + + uint8_t unit = 0; + uint8_t port_num = 0; + + unit = strtol(argv[0], NULL, 10) - 1; + port_num = strtol(argv[1], NULL, 10); + + _dau_add(unit, port_num); + + return CMD_SUCCESS; +} + +CMD(no_dau_add, + no_dau_add_cmd, + "no dau <1-2>", + "no\n" + "DAU\n" + "Unit id\n" + "Port\n" + "Port number\n") +{ + return CMD_SUCCESS; + + uint8_t unit = 0; + + unit = strtol(argv[0], NULL, 10) - 1; + + _dau_del(unit); + + return CMD_SUCCESS; +} + +/* DAU 同步主备方式. */ +CMD(dau_master_slave_mode, + dau_master_slave_mode_cmd, + "dau <1-2> mode (auto|master|slave)", + "DAU\n" + "Unit id\n" + "Master slave mode\n" + "auto\n" + "master\n" + "slave\n") +{ + return CMD_SUCCESS; + + dau_t *dau_node = NULL; + uint8_t unit = 0; + uint8_t mode = 0; + uint16_t temp = 0; + uint16_t GCR = 0; + + unit = strtol(argv[0], NULL, 10) - 1; + + dau_node = dau[unit]; + if (!dau_node) + { + return CMD_WARNING; + } + + for(mode = PD_DAU_MODE_AUTO; mode < PD_DAU_MODE_COUNT; mode++) + { + if (strncmp(argv[1], dau_mode_str[mode], strlen(dau_mode_str[mode]))) + { + continue; + } + + GCR = dau_node->reg.GCR; + temp = (mode << DAU_GCR_MODE_Pos); + MODIFY_REG(dau_node->reg.GCR, DAU_GCR_MODE_Msk, temp); + } + + /* 配置恢复过程中不进行寄存器下发. */ + if (is_system_init) + { + if (dau_reg_global_write(unit) != E_NONE) + { + dau_node->reg.GCR = GCR; + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +/* 配置端口模式. */ +CMD(dau_port_type, + dau_port_type_cmd, + "port-type (uhf|ae|tev|hf)", + "DAU port type\n" + "UHF\n" + "AE\n" + "TEV\n" + "HF\n") +{ + return CMD_SUCCESS; + + dau_t *dau_node = NULL; + uint8_t unit = 0; + uint8_t port = 0; + uint8_t port_type = 0; + uint16_t temp = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + for(port_type = PD_PORT_TYPE_UHF; port_type < PD_PORT_TYPE_COUNT; port_type++) + { + if (strncmp(argv[0], dau_port_type_str[port_type], strlen(dau_port_type_str[port_type]))) + { + continue; + } + + dau_node = dau[unit]; + temp = (port_type << DAU_CR_PT_Pos); + MODIFY_REG(dau_node->port_reg[port].CR, DAU_CR_PT_Msk, temp); + + if (PD_PORT_TYPE_UHF == port_type) + { + dau_node->port_func[port] = &gis_func; + } + else + { + dau_node->port_func[port] = &gis_func; + } + } + + return CMD_SUCCESS; +} + +/* 通道采样频率模式. */ +CMD(dau_sample_rate, + dau_sample_rate_cmd, + "sample-rate <1-50>", + "Port sample rate\n" + "Sample rate\n") +{ + return CMD_SUCCESS; + + dau_t *dau_node = NULL; + uint8_t unit = 0; + uint8_t port = 0; + uint8_t rate = 0; + uint16_t temp = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + rate = strtol(argv[0], NULL, 10); + if (DAU_ADC_RATE % rate) + { + return CMD_WARNING; + } + rate = DAU_ADC_RATE / rate; + + dau_node = dau[unit]; + temp = (rate << DAU_CR_SR_Pos); + MODIFY_REG(dau_node->port_reg[port].CR, DAU_CR_SR_Msk, temp); + pd_config.port_config[unit][port].config.sample_rate = rate * 1000000; + + return CMD_SUCCESS; +} + +/* 通道滤波类型. */ +CMD(dau_filter_type, + dau_filter_type_cmd, + "filtrate-type (none|ff|fr|lf|hf)", + "Port filtrate type\n" + "None\n" + "Full frequency \n" + "Full frequency resistor\n" + "Low frequency\n" + "High frequency\n") +{ + return CMD_SUCCESS; + + dau_t *dau_node = NULL; + uint8_t unit = 0; + uint8_t port = 0; + uint8_t filter = 0; + uint8_t filter_csg = 0; + uint16_t temp = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + if (0 == strncmp(argv[0], "hf", 2)) + { + filter = PD_FILTER_TYPE_HF; + filter_csg = CSG_FILTER_TYPE_HF; + } + else if (0 == strncmp(argv[0], "lf", 2)) + { + filter = PD_FILTER_TYPE_LF; + filter_csg = CSG_FILTER_TYPE_LF; + } + else if (0 == strncmp(argv[0], "fr", 2)) + { + filter = PD_FILTER_TYPE_FR; + filter_csg = CSG_FILTER_TYPE_FR; + } + else if (0 == strncmp(argv[0], "ff", 2)) + { + filter = PD_FILTER_TYPE_FF; + filter_csg = CSG_FILTER_TYPE_FF; + } + else + { + filter = 0; + } + + dau_node = dau[unit]; + temp = (filter << DAU_FTR_FT_Pos); + MODIFY_REG(dau_node->port_reg[port].FTR, DAU_FTR_FT_Msk, temp); + pd_config.port_config[unit][port].config.filter = filter_csg; + pd_config.port_config[unit][port].filter_cfg = filter_csg; + + return CMD_SUCCESS; +} + +/* 通道滤波类型. */ +CMD(dau_port_sync, + dau_port_sync_cmd, + "port-sync", + "DAU port reg sync\n") +{ + return CMD_SUCCESS; + + uint8_t unit = 0; + uint8_t port = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + if (dau_reg_port_write(unit, port) != E_NONE) + { + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* 显示 DAU 状态. */ +CMD(show_dau_status_all, + show_dau_status_all_cmd, + "show dau", + "Show\n" + "DAU\n") +{ + return CMD_SUCCESS; + + uint8_t unit = 0; + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_show(unit); + } + + return CMD_SUCCESS; +} + +/* 显示 DAU 校准系数. */ +CMD(show_dau_adj_all, + show_dau_adj_all_cmd, + "show dau adj", + "Show\n" + "DAU\n" + "ADJ\n") +{ + return CMD_SUCCESS; + + uint8_t unit = 0; + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_show_adj(unit); + } + + return CMD_SUCCESS; +} + +/* 显示 DAU 全局寄存器. */ +CMD(show_dau_reg_all, + show_dau_reg_all_cmd, + "show dau reg", + "Show\n" + "DAU\n" + "Reg\n") +{ + return CMD_SUCCESS; + + uint8_t unit = 0; + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_show_reg(unit); + } + + return CMD_SUCCESS; +} + +/* 显示 DAU 全局寄存器. */ +CMD(show_dau_reg_port, + show_dau_reg_port_cmd, + "show dau port <1-16>", + "Show\n" + "DAU\n" + "Port Reg\n" + "Port number") +{ + return CMD_SUCCESS; + + uint8_t vport = 0; + uint8_t unit = 0; + uint8_t port = 0; + + vport = strtol(argv[0], NULL, 10); + + /* 取出端口号. */ + if (dau_vport_to_port(vport, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + dau_show_reg_port(unit, port); + + return CMD_SUCCESS; +} + +/* 根据 IP 查找 DAU. */ +int32_t _dau_unit_get_by_ip(uint32_t ip, uint8_t *unit) +{ + uint8_t i = 0; + bool is_find = FALSE; + + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + if (ip == dau[i]->ip_addr) + { + *unit = i; + is_find = TRUE; + break; + } + + + } + + if (is_find) + { + return E_NONE; + } + else + { + return E_BAD_PARAM; + } +} + +/* 根据 IP 查找 DAU. */ +int32_t _dau_unit_get_by_slave(uint8_t id, uint8_t *unit) +{ + uint8_t i = 0; + bool is_find = FALSE; + + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + if (id == dau[i]->unit) + { + *unit = dau[i]->unit; + is_find = TRUE; + break; + } + } + + if (is_find) + { + return E_NONE; + } + else + { + return E_BAD_PARAM; + } +} + +int32_t _dau_lock_timeout(uint8_t unit, uint32_t sec, uint32_t nsec) +{ + struct timespec time_out; + dau_t *dau_node = dau[unit]; + + clock_gettime(CLOCK_REALTIME, &time_out); + time_out.tv_sec += sec; + time_out.tv_nsec += nsec; + if (time_out.tv_nsec >= 1000000000) + { + time_out.tv_nsec -= 1000000000; + time_out.tv_sec++; + } + + return pthread_mutex_timedlock(&dau_node->mutex, &time_out); +} + +void _dau_unlock(uint8_t unit) +{ + dau_t *dau_node = dau[unit]; + + pthread_mutex_unlock(&dau_node->mutex); +} + +/* 复位 fpga. */ +void _dau_reset(void) +{ + //FPGAResetEnable + usleep(1000); + //FPGAResetDisable +} + +/* 读 DAU 寄存器. */ +int32_t _dau_reg_read(uint8_t unit, uint16_t addr, uint16_t len) +{ + dau_send_msg_t send_msg; + dau_pkt_t *pkt = NULL; + dau_t *dau_node = dau[unit]; + uint16_t data_len = 8; + + if (dau_ctrl.is_update) + { + return E_NONE; + } + + if (!dau_node->is_valid) + { + return E_NONE; + } + + /* 申请报文空间, 8是报文总长度. */ + pkt = XMALLOC_Q(MTYPE_DAU, data_len); + if (!pkt) + { + return E_MEM; + } + + /* 组装报文. */ + pkt->slave_id = 0x0B; + pkt->func = 0x01; + pkt->addr = addr; + pkt->len = len; + pkt->data[0] = crc16_modbus((uint8_t*)pkt, data_len - 2); + + /* 封装消息. */ + memset(&send_msg, 0, sizeof(dau_send_msg_t)); + send_msg.type = dau_node->msg_send_type; + send_msg.data.send_type = DAU_SEND_TYPE_REG_RD; + send_msg.data.send_len = data_len; + send_msg.data.data = pkt; + + /* 发送消息 */ + if ((msgsnd(recv_qid, &send_msg, sizeof(dau_send_msg_data_t), 0)) < 0) + { + DBG(DBG_M_PD_DAU_ERR, "DAU msgsnd ERROR return %s!\r\n", safe_strerror(errno)); + XFREE(MTYPE_DAU, pkt); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 写 DAU 寄存器. */ +int32_t _dau_reg_write(uint8_t unit, uint16_t addr, uint16_t len, uint16_t *data) +{ + dau_send_msg_t send_msg; + dau_pkt_t *pkt = NULL; + dau_t *dau_node = dau[unit]; + uint16_t data_len = 8 + (len << 1); + + if (dau_ctrl.is_update) + { + return E_NONE; + } + + if (!dau_node->is_valid) + { + return E_NONE; + } + + /* 申请报文空间 */ + pkt = XMALLOC_Q(MTYPE_DAU, data_len); + if (!pkt) + { + return E_MEM; + } + + /* 组装报文. */ + pkt->slave_id = 0x0B; + pkt->func = DAU_PKT_CMD_WR; + pkt->addr = addr; + pkt->len = len; + memcpy(pkt->data, data, (len << 1)); + pkt->data[len] = crc16_modbus((uint8_t*)pkt, data_len - 2); + + /* 封装消息. */ + memset(&send_msg, 0, sizeof(dau_send_msg_t)); + send_msg.type = dau_node->msg_send_type; + send_msg.data.send_type = DAU_SEND_TYPE_REG_WR; + send_msg.data.send_len = data_len; + send_msg.data.data = pkt; + + /* 发送消息. */ + if ((msgsnd(recv_qid, &send_msg, sizeof(dau_send_msg_data_t), 0)) < 0) + { + DBG(DBG_M_PD_DAU_ERR, "DAU msgsnd ERROR return %s!\r\n", safe_strerror(errno)); + XFREE(MTYPE_DAU, pkt); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 全局寄存器读取. */ +int32_t _dau_reg_global_read(uint8_t unit) +{ + DBG(DBG_M_PD_DAU, "Entry: unit %d\r\n", unit); + + LD_E_RETURN(DBG_M_PD_DAU_ERR, _dau_reg_read(unit, DAU_REG_ADDR_GSCR, sizeof(dau_reg_t) >> 1)); + + DBG(DBG_M_PD_DAU, "Leave\r\n"); + return E_NONE; +} + +/* 端口寄存器读取. */ +int32_t _dau_reg_port_read(uint8_t unit, uint8_t port) +{ + uint16_t addr = DAU_REG_PORT_ADDR_GET(port); + + DBG(DBG_M_PD_DAU, "Entry: unit %d port %d\r\n", unit, port); + + LD_E_RETURN(DBG_M_PD_DAU_ERR, _dau_reg_read(unit, addr, sizeof(dau_port_reg_t) >> 1)); + + DBG(DBG_M_PD_DAU, "Leave\r\n"); + return E_NONE; +} + +/* 读通道校准系数. */ +int32_t _dau_param_get(uint8_t unit) +{ + uint8_t port = 0; + + DBG(DBG_M_PD_DAU, "Entry: unit %d\r\n", unit); + + for(port = 0; port < PD_DAU_PORT_SUM; port++) + { + LD_E_RETURN(DBG_M_PD_DAU_ERR, _dau_reg_port_read(unit, port)); + } + + DBG(DBG_M_PD_DAU, "Leave\r\n"); + return E_NONE; +} + +/* 每秒获取 DAU 状态寄存器. */ +void* _dau_time_state_get(void *arg) +{ + dau_t *dau_node = (dau_t*)arg; + + /* 读取端口状态寄存器, 长度 16. */ + _dau_reg_global_read(dau_node->unit); + + mtimer_add(_dau_time_state_get, (void *)dau_node, 10, "DAU_STATE_GET"); + + return NULL; +} + +/* DAU 模块添加处理函数. */ +int32_t _dau_add(uint8_t unit, uint8_t port_num) +{ + dau_t *dau_node = dau[unit]; + pthread_t pid; + struct sched_param param; + pthread_attr_t attr; + char name[THREAD_M_NAME_LEN]; + char ip[32]; + uint8_t i = 0; + uint16_t temp = 0; + int32_t rv = 0; + + DBG(DBG_M_PD_DAU, "Entry: unit %d port_num %d\r\n", unit, port_num); + + /* 申请配置节点空间. */ + if (!dau_node) + { + dau_node = XMALLOC(MTYPE_DAU, sizeof(dau_t)); + if (!dau_node) + { + dau_node = NULL; + return E_MEM; + } + } + else + { + return E_NONE; + } + + /* 初始化 DAU 结构体. */ + dau_node->unit = unit; + dau_node->port_num = port_num; + snprintf(ip, 32, "192.168.7.%d", unit + 10); + dau_node->ip_addr = inet_addr(ip); + dau_node->ip_port = 7001; + dau_node->msg_send_type = unit + 1; + dau_node->is_connect = TRUE; + for(i = 0; i < dau_node->port_num; i++) + { + temp = (PD_PORT_TYPE_UHF << DAU_CR_PT_Pos); + MODIFY_REG(dau_node->port_reg[i].CR, DAU_CR_PT_Msk, temp); + temp = (1 << DAU_CR_SR_Pos); + MODIFY_REG(dau_node->port_reg[i].CR, DAU_CR_SR_Msk, temp); + temp = (PD_FILTER_TYPE_FR << DAU_FTR_FT_Pos); + MODIFY_REG(dau_node->port_reg[i].FTR, DAU_FTR_FT_Msk, temp); + dau_node->port_func[i] = &gis_func; + } + dau[unit] = dau_node; + + /* 初始化配置端口号. */ + for(i = 0; i < dau_node->port_num; i++) + { + pd_config.port_config[unit][i].config.vport = dau_port_to_vport(unit, i); + } + + mtimer_add(_dau_time_state_get, (void *)dau[unit], 10, "DAU_STATE_GET"); + + /* 初始化线程锁, 用于发包线程与首保线程同步. */ + pthread_mutex_init(&dau_node->mutex, NULL); + pthread_mutex_lock(&dau_node->mutex); + + /* 初始化发送线程. */ + /* 配置线程RR调度, 优先级78 */ + pthread_attr_init(&attr); + param.sched_priority = 78; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _dau_send_handle, dau_node); + if (rv != 0) + { + log_err(LOG_DAU, "DAU%d can't create send pthread %d!", unit, rv); + XFREE(MTYPE_DAU, dau_node); + dau_node = NULL; + return E_SYS_CALL; + } + else + { + snprintf(name, THREAD_M_NAME_LEN, "DAU_SEND_%d", unit); + thread_m_add(name, pid); + } + pthread_attr_destroy(&attr); + + /* 初始化后, 添加 DAU, 需要重启DAU. */ + if (is_system_init) + { + dau_shutdown(); + dau_start(); + } + + DBG(DBG_M_PD_DAU, "Leave\r\n"); + return E_NONE; +} + +/* DAU 模块删除处理函数. */ +void _dau_del(uint8_t unit) +{ + dau_t *dau_node = dau[unit]; + + if (!dau_node) + { + return; + } + + dau_node->is_valid = FALSE; +} + +/* 发送数据到后台通讯进程. */ +int32_t _dau_msg_send(uint32_t type, void *data) +{ + pd_csg_msg_t up_msg; + + /* 封装消息. */ + up_msg.type = type; + up_msg.data = data; + + /* 发送消息 */ + if (fifo_write(csg.data_fifo_id, (void*)(&up_msg), sizeof(pd_csg_msg_t)) != sizeof(pd_csg_msg_t)) + { + DBG(DBG_M_PD_DAU_ERR, "DAU write ERROR!\r\n"); + return E_ERROR; + } + + return E_NONE; +} + +/* 发送数据到本地存储进程 */ +int32_t _dau_msg_send_storage(uint32_t type, void *data) +{ + pd_storage_msg_t storage_msg; + + /* 封装消息. */ + storage_msg.type = type; + storage_msg.data = data; + + /* 发送消息 */ + if (fifo_write(storage.storage_fifo_id, (void*)(&storage_msg), sizeof(pd_storage_msg_t)) != sizeof(pd_storage_msg_t)) + { + DBG(DBG_M_PD_DAU_ERR, "DAU write ERROR!\r\n"); + return E_ERROR; + } + + return E_NONE; +} + +/* PRPS 数据完整性检查. */ +void _dau_prps_complete_check(void) +{ + uint8_t unit = 0; + uint8_t port = 0; + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + for(port = 0; port < dau[unit]->port_num; port++) + { + if (dau[unit]->port_state[port].prps_index != 0) + { + dau[unit]->port_state[port].is_complete = FALSE; + if (!dau_ctrl.data_err_cnt) + { + log_err(LOG_DAU, "DAU%d port %d prps ERRORR!", unit, port); + } + dau_ctrl.data_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d port %d prps ERRORR!\r\n", unit, port); + } + else + { + dau[unit]->port_state[port].is_complete = TRUE; + } + } + } +} + +/* 发送降噪数据到后台通讯进程. */ +void _dau_prps_denoise_send(void) +{ + if (_dau_msg_send(PD_SEND_TYPE_PRPS, pd_data.denoise) != E_NONE) + { + XFREE(MTYPE_CSG, pd_data.denoise); + } +} + +/* PRPS 数据实时显示数据降噪处理. */ +void _dau_denoise_process(void) +{ + dau_t *dau_node = NULL; + uint8_t unit = 0; + uint8_t port = 0; + + /* 没有端口被关注或者没有连接服务器不用计算. */ + if (!pd_config.concern_bitmap + || !csg.is_connect) + { + return; + } + + /* 申请 PRPS 数据空间. */ + pd_data.denoise = XMALLOC_Q(MTYPE_CSG, sizeof(pd_prps_t)); + if (!pd_data.denoise) + { + return; + } + + /* 自动 / 手动降噪 (PRPS 显示). */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->denoise) + { + continue; + } + + dau_node->port_func[port]->denoise(unit, port); + } + } + + /* 关联降噪 (PRPS 显示). */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->denoise_r) + { + continue; + } + + dau_node->port_func[port]->denoise_r(unit, port); + } + } + + /* 降噪数据统计 (PRPS 显示). */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->denoise_state) + { + continue; + } + + dau_node->port_func[port]->denoise_state(unit, port); + } + } + + /* 将降噪后的 PRPS 数据发送到后台服务器. */ + _dau_prps_denoise_send(); +} + +/* 实时数据降噪处理. */ +void _dau_real_process(void) +{ + static uint16_t prps_sec = 0; + pd_prps_t *storaged_data = NULL; + dau_t *dau_node = NULL; + uint8_t unit = 0; + uint8_t port = 0; + uint32_t now = time(NULL); + + /* 定时PRPS计算 */ + prps_sec++; + + /* 自动 / 手动降噪. */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->denoise_real) + { + continue; + } + + dau_node->port_func[port]->denoise_real(unit, port); + } + } + + /* 关联降噪. */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->denoise_real_r) + { + continue; + } + + dau_node->port_func[port]->denoise_real_r(unit, port); + } + } + + /* 降噪数据统计 (PRPS 显示). */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->denoise_real_state) + { + continue; + } + + dau_node->port_func[port]->denoise_real_state(unit, port); + } + } + + pd_data.real.index = dau_ctrl.real_idx++; + pd_data.real.utc = now; + pd_data.real.is_denoise = TRUE; + + /* 将降噪后的 PRPS 数据发送到本地存储进程 */ + if (prps_sec >= pd_config.config.real_period * 60) + { + prps_sec = 0; + storaged_data = XMALLOC_Q( MTYPE_STORAGE, sizeof(pd_prps_t) ); + if (!storaged_data) + { + return; + } + + memcpy( storaged_data, &pd_data.real, sizeof(pd_prps_t) ); + if (_dau_msg_send_storage(STORAGE_TYPE_FIXEDTIMERPRPS, storaged_data) != E_NONE) + { + XFREE(MTYPE_STORAGE, storaged_data); + } + } +} + +/* 事件计算. */ +void _dau_event_process(void) +{ + dau_t *dau_node = NULL; + pd_event_t *data = NULL; + pd_event_t *storaged_data = NULL; + uint8_t unit = 0; + uint8_t port = 0; + + /* 事件计算. */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->event) + { + continue; + } + + dau_node->port_func[port]->event(unit, port); + + if (pd_data.event[unit][port].cnt <= pd_config.port_config[unit][port].config.event_counter) + { + continue; + } + + /* 事件处理. */ + memcpy(pd_data.event[unit][port].data, pd_data.real.data[unit][port].data, PD_PRPS_NUM << 1); + pd_data.event[unit][port].vport = dau_port_to_vport(unit, port); + pd_data.event[unit][port].second = 1; + pd_data.event[unit][port].index = dau_ctrl.event_index[unit][port]++; + pd_data.event[unit][port].utc = time(NULL); + + data = XMALLOC_Q(MTYPE_CSG, sizeof(pd_event_t)); + if (!data) + { + return; + } + + memcpy(data, &pd_data.event[unit][port], sizeof(pd_event_t)); + if (_dau_msg_send(PD_SEND_TYPE_EVENT, data) != E_NONE) + { + XFREE(MTYPE_CSG, data); + } + + storaged_data = XMALLOC_Q(MTYPE_STORAGE, sizeof(pd_event_t)); + if (!storaged_data) + { + return; + } + + memcpy(storaged_data, &pd_data.event[unit][port], sizeof(pd_event_t)); + if (_dau_msg_send_storage(STORAGE_TYPE_EVENT, storaged_data) != E_NONE) + { + XFREE(MTYPE_STORAGE, storaged_data); + } + } + } +} + +/* 趋势数据处理. */ +void _dau_trend_process(void) +{ + static uint16_t trend_sec = 0; + dau_t *dau_node = NULL; + pd_trend_t *data = NULL; + pd_trend_t *storaged_data = NULL; + uint8_t unit = 0; + uint8_t port = 0; + + /* 事件计算. */ + trend_sec++; + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + for(port = 0; port < dau_node->port_num; port++) + { + if (!dau_node->port_func[port]->trend) + { + continue; + } + + + dau_node->port_func[port]->trend(unit, port, trend_sec); + } + } + + if (trend_sec >= pd_config.config.trend_period * 60) + { + trend_sec = 0; + data = XMALLOC_Q(MTYPE_CSG, sizeof(pd_trend_t)); + if (!data) + { + return; + } + + memcpy(data, &pd_data.trend, sizeof(pd_trend_t)); + if (_dau_msg_send(PD_SEND_TYPE_TREND, data) != E_NONE) + { + XFREE(MTYPE_CSG, data); + } + + storaged_data = XMALLOC_Q(MTYPE_STORAGE, sizeof(pd_trend_t)); + if (!storaged_data) + { + return; + } + + memcpy(storaged_data, &pd_data.trend, sizeof(pd_trend_t)); + if (_dau_msg_send_storage(STORAGE_TYPE_TREND, storaged_data) != E_NONE) + { + XFREE(MTYPE_STORAGE, storaged_data); + } + } +} + +/* 1s prps 数据处理. */ +void _dau_prps_process(void) +{ + static uint8_t data_cnt = 0; + + /* 上电非稳定期间的数据丢弃, 收到 10 次端口 1 的数据, 大概 10s. */ + if (data_cnt < 5) + { + data_cnt++; + return; + } + + /* PRPS 数据实时显示数据处理. */ + _dau_denoise_process(); + + /* 实时数据降噪处理. */ + _dau_real_process(); + + /* 事件处理. */ + _dau_event_process(); + + /* 趋势数据处理 */ + _dau_trend_process(); +} + +/* 报文头检查. */ +int32_t _dau_pkt_check(dau_pkt_t *pkt, uint32_t len) +{ + if ((pkt->len << 1) + 8 != len + && pkt->func != DAU_PKT_CMD_WR + && pkt->func != DAU_PKT_CMD_UPD) + { + return E_ERROR; + } + + /* 验证 CRC32. */ + //if (crc32(cmd, head->len) != (*(uint32_t*)(cmd + head->len))) + //{ + // vty_print("@3: %x %x\r\n", crc32(cmd, head->len), *(uint32_t*)(cmd + head->len)); + // return HAL_ERROR; + //} + + return E_NONE; +} + +/* PRPS 数据处理. */ +int32_t _dau_pkt_prps(uint8_t unit, dau_pkt_t *pkt, uint32_t len) +{ + static bool is_updata = FALSE; + uint8_t port = pkt->addr; + dau_pkt_prps_t *data = (dau_pkt_prps_t *)pkt->data; + dau_t *dau_node = dau[unit]; + dau_port_state_t *port_state = NULL; + + /* 参数检查. */ + if (port >= dau_node->port_num) + { + return E_ERROR; + } + + if (len != 1292) + { + return E_ERROR; + } + + /* 检查是否连续, 不连续说明丢包. */ + port_state = &dau_node->port_state[port]; + /* 预留调试打印, 用于查看板卡报文是否同步. */ + //struct timeval tv; + //if (port == 3) + //{ + // gettimeofday(&tv, NULL); + // printf("%d %d %d/t %d\r\n", unit, tv.tv_sec, tv.tv_usec, data->power_fre_cnt); + // //printf("%d\r\n", tv.tv_usec); + //} + if (port_state->prps_cnt != data->power_fre_cnt) + { + port_state->prps_index = (data->power_fre_cnt % 50) / 5; + if (!dau_ctrl.data_err_cnt) + { + log_err(LOG_DAU, "DAU%d utc %d_%d ERRORR!", unit, port_state->prps_cnt, data->power_fre_cnt); + } + dau_ctrl.data_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d utc %d_%d ERRORR!\r\n", unit, port_state->prps_cnt, data->power_fre_cnt); + } + + /* 检查是否读到完整的 1s 数据. */ + if (0 == port_state->prps_index) + { + /* 保证只在 0 == port_state->prps_index 时只进入一次.*/ + if (is_updata) + { + is_updata = FALSE; + _dau_prps_complete_check(); + _dau_prps_process(); + } + + dau_node->port_func[port]->data_default(port_state->prps); + } + + /* 复制数据. */ + memcpy(port_state->prps + port_state->prps_index * DAU_PKT_PRPS_LEN, data->data, DAU_PKT_PRPS_DATA_LEN); + + /* 更新状态 */ + port_state->prps_cnt = data->power_fre_cnt + 5; + if (port_state->prps_cnt >= 2000000000) + { + port_state->prps_cnt = 0; + } + port_state->prps_index++; + if (5 == port_state->prps_index) + { + is_updata = TRUE; + } + else if (10 == port_state->prps_index) + { + port_state->prps_index = 0; + } + + return E_NONE; +} + +/* 读取寄存器处理. */ +int32_t _dau_pkt_reg_rd(uint8_t unit, dau_pkt_t *pkt, uint32_t len) +{ + dau_t *dau_node = dau[unit]; + uint8_t port = 0; + + /* 保存数据. */ + if (DAU_REG_ADDR_GSCR == pkt->addr) + { + memcpy(&dau_node->reg_dau, pkt->data, pkt->len << 1); + dau_node->reg.GSCR = dau_node->reg_dau.GSCR; + dau_node->reg.GSVR = dau_node->reg_dau.GSVR; + dau_node->reg.GSR = dau_node->reg_dau.GSR; + dau_node->reg.GPFPCCR = dau_node->reg_dau.GPFPCCR; + dau_node->reg.GPFPCR = dau_node->reg_dau.GPFPCR; + dau_node->reg.GNSCR = dau_node->reg_dau.GNSCR; + } + else if (DAU_REG_PORT_ADDR_MSRR == pkt->addr) + { + memcpy(&dau_node->port_state_reg, pkt->data, pkt->len << 1); + debug_pkt_port_state_post(); + } + else if(DAU_REG_PORT_BASE <= pkt->addr && pkt->addr < DAU_REG_PORT_ADDR_MSRR) + { + port = pkt->addr >> 12; + memcpy(&dau_node->port_reg_dau[port - 1].CR, pkt->data, pkt->len << 1); + memcpy(&dau_node->port_reg[port - 1].ASPR[0], &dau_node->port_reg_dau[port - 1].ASPR[0], 60); + } + + if (DAU_SEND_TYPE_REG_RD == dau_node->send_type) + { + dau_node->send_type = 0; + _dau_unlock(unit); + } + + return E_NONE; +} + +/* 写寄存器处理. */ +int32_t _dau_pkt_reg_wr(uint8_t unit, dau_pkt_t *pkt, uint32_t len) +{ + dau_t *dau_node = dau[unit]; + + if (DAU_SEND_TYPE_REG_WR == dau_node->send_type) + { + dau_node->send_type = 0; + _dau_unlock(unit); + } + + return E_NONE; +} + +/* 读取寄存器处理. */ +int32_t _dau_pkt_update(uint8_t unit, dau_pkt_t *pkt, uint32_t len) +{ + dau_t *dau_node = dau[unit]; + + /* 保存数据. */ + if (dau_ctrl.update_idx != pkt->addr) + { + return E_ERROR; + } + + dau_ctrl.update_idx++; + + if (DAU_SEND_TYPE_UPDATE == dau_node->send_type) + { + _dau_unlock(unit); + } + + return E_NONE; +} + +/* DAU 报文处理 */ +int32_t _dau_data_process(uint32_t ip, dau_pkt_t *pkt, uint32_t len) +{ + uint8_t unit = 0; + + /* 查找 DAU. */ + if (_dau_unit_get_by_slave(pkt->slave_id, &unit) != E_NONE) + { + if (!dau_ctrl.data_err_cnt) + { + log_err(LOG_DAU, "DAU%d id %d ERRORR!", unit, pkt->slave_id); + } + dau_ctrl.data_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d id %d ERRORR!\r\n", unit, pkt->slave_id); + return E_BAD_PARAM; + } + + if (!dau[unit]->is_valid) + { + return E_NONE; + } + + /* 报文头和 CRC 校验. */ + if (_dau_pkt_check(pkt, len) != E_NONE) + { + if (!dau_ctrl.data_err_cnt) + { + log_err(LOG_DAU, "DAU%d len %d_%d ERRORR!", unit, pkt->len, len); + } + dau_ctrl.data_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d len %d_%d ERRORR!\r\n", unit, pkt->len, len); + return E_BAD_PARAM; + } + + /* 共有命令处理. */ + switch (pkt->func) + { + case DAU_PKT_CMD_RD: + _dau_pkt_reg_rd(unit, pkt, len); + break; + case DAU_PKT_CMD_WR: + case DAU_PKT_CMD_SAV: + _dau_pkt_reg_wr(unit, pkt, len); + break; + case DAU_PKT_CMD_UPD: + _dau_pkt_update(unit, pkt, len); + break; + case DAU_PKT_CMD_PRPS: + _dau_pkt_prps(unit, pkt, len); + break; + default: + if (!dau_ctrl.data_err_cnt) + { + log_err(LOG_DAU, "DAU%d func %d ERRORR!", unit, pkt->func); + } + dau_ctrl.data_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d func %d ERRORR!\r\n", unit, pkt->func); + break; + } + + return E_NONE; +} + +int32_t _dau_send_handle_pkt(dau_t *dau_node, dau_send_msg_t *send_msg) +{ + struct sockaddr_in server; + uint8_t err_cnt = 0; + int32_t rv = 0; + + /* 发送数据. */ + bzero(&server, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = dau_node->ip_addr; + server.sin_port = htons(dau_node->ip_port); + err_cnt = 0; + while(1) + { + /* 多次发送失败退出发送. */ + if (DAU_SEND_ERR_MAX == err_cnt) + { + if (!dau_ctrl.send_err_cnt) + { + log_err(LOG_DAU, "DAU%d send ERRORR!", dau_node->unit); + } + dau_ctrl.send_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d send ERRORR!\r\n", dau_node->unit); + break; + } + + /* 调试打印报文. */ + DBG(DBG_M_PD_DAU_SEND, "DAU(%d) send(%d):\r\n", dau_node->unit, send_msg->data.send_len); + if (dbg_stat_get(DBG_M_PD_DAU_SEND)) + { + buf_print( send_msg->data.data, send_msg->data.send_len > 32 ? 32 : send_msg->data.send_len); + printh("\r\n"); + } + + /* 发送数据. */ + rv = sendto(dau_ctrl.fd, (char*) send_msg->data.data, send_msg->data.send_len, 0, (struct sockaddr*)&server, sizeof(server)); + if (rv < 0) + { + err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d sendto ERRORR!\r\n", dau_node->unit); + continue; + } + + /* 等待命令完成. */ + rv = _dau_lock_timeout(dau_node->unit, 0, DAU_SEND_TIMEOUT); + /* 超时处理. */ + if (rv != 0) + { + err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d timeout ERRORR!\r\n", dau_node->unit); + continue; + } + + return E_NONE; + } + + return E_ERROR; +} + +int32_t _dau_send_handle_update(dau_t *dau_node, dau_send_msg_t *send_msg) +{ + struct sockaddr_in server; + dau_pkt_t *pkt = (dau_pkt_t*)send_msg->data.data; + char *data = (char*)pkt->data; + uint8_t is_end = 0; + uint8_t err_cnt = 0; + int fd = 0; + uint16_t len = 0; + int32_t rv = 0; + + fd = open(DEBUG_DAU_FILE, O_RDONLY); + if (fd <= 0) + { + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d open ERR!\r\n", dau_node->unit); + dau_ctrl.update_ret = -1; + return E_SYS_CALL; + } + + /* 发送数据. */ + bzero(&server, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = dau_node->ip_addr; + server.sin_port = htons(dau_node->ip_port); + err_cnt = 0; + while(1) + { + /* 多次发送失败退出发送. */ + if (DAU_SEND_ERR_MAX == err_cnt) + { + if (!dau_ctrl.send_err_cnt) + { + log_err(LOG_DAU, "DAU%d send ERRORR!", dau_node->unit); + } + dau_ctrl.send_err_cnt++; + + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d send ERRORR!\r\n", dau_node->unit); + dau_ctrl.update_ret = -2; + break; + } + + /* 组装报文. */ + pkt->slave_id = 0x0B; + pkt->func = DAU_PKT_CMD_UPD; + pkt->addr = dau_ctrl.update_idx; + pkt->len = 512; + len = read(fd, data, 1024); + if (len < 0) + { + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d read ERR!\r\n", dau_node->unit); + dau_ctrl.update_ret = -3; + break; + } + else if (0 == len) + { + dau_ctrl.update_ret = DAU_UPD_OK; + close(fd); + return E_NONE; + } + else if (len < 1024) + { + memset(data + len, 0xff, 1024 - len); + is_end = TRUE; + } + pkt->data[512] = crc16_modbus((uint8_t*)pkt, send_msg->data.send_len - 2); + + /* 发送数据. */ + rv = sendto(dau_ctrl.fd, (char*) send_msg->data.data, send_msg->data.send_len, 0, (struct sockaddr*)&server, sizeof(server)); + if (rv < 0) + { + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d sendto ERR!\r\n", dau_node->unit); + err_cnt++; + continue; + } + + /* 等待命令完成. */ + rv = _dau_lock_timeout(dau_node->unit, DAU_SEND_TIMEOUT_U, 0); + /* 超时处理. */ + if (rv != 0) + { + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d timeout ERR!\r\n", dau_node->unit); + err_cnt++; + continue; + } + + if (is_end) + { + dau_ctrl.update_ret = DAU_UPD_OK; + close(fd); + return E_NONE; + } + } + + close(fd); + return E_ERROR; +} + +/* DAU 报文发送处理函数. */ +void *_dau_send_handle(void *arg) +{ + dau_t *dau_node = (dau_t*)arg; + dau_send_msg_t send_msg; + uint8_t adu_err_cnt = 0; + int32_t rv = 0; + + /* 主循环. */ + while (1) + { + /* 读取消息队列. */ + memset(&send_msg.data, 0, sizeof(dau_send_msg_data_t)); + if (-1 == msgrcv(recv_qid, (void*)&send_msg, sizeof(dau_send_msg_data_t), dau_node->msg_send_type, 0)) + { + if (!dau_ctrl.send_err_cnt) + { + log_err(LOG_DAU, "DAU%d msgrcv ERRORR!", dau_node->unit); + } + dau_ctrl.send_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d msgrcv ERRORR!\r\n", dau_node->unit); + continue; + } + + /* 记录当前的发送状态, 用于在收包时判断是否释放信号量. */ + dau_node->send_type = send_msg.data.send_type; + + /* 发送报文. */ + if (DAU_SEND_TYPE_UPDATE == dau_node->send_type) + { + rv = _dau_send_handle_update(dau_node, &send_msg); + } + else + { + rv = _dau_send_handle_pkt(dau_node, &send_msg); + } + + /* 记录 DAU 连接状态. */ + if (rv == E_NONE) + { + adu_err_cnt = 0; + } + else + { + if (adu_err_cnt <= DAU_ERR_MAX) + { + adu_err_cnt++; + } + } + + dau_node->is_connect = (adu_err_cnt < DAU_ERR_MAX); + + /* 释放数据内存. */ + XFREE(MTYPE_DAU, send_msg.data.data); + } + + return NULL; +} + +/* DAU 报文接收处理函数. */ +void *_dau_recv_handle(void *arg) +{ + struct sockaddr_in server; + socklen_t server_len; + dau_recv_msg_data_t recv_msg; + char *pkt = NULL; + uint16_t data_len = 0; + //dau_pkt_t *temp = NULL; + //uint16_t temp1 = 0; + +#ifdef DAU_IPC_FIFO + int fd = 0; + + fd = open(DAU_RECV_FIFO, O_WRONLY | O_CREAT, 0644); + if (fd == -1) + { + log_err(LOG_DAU, "Open write fifo file " DAU_RECV_FIFO " error!"); + return NULL; + } +#endif + + while(1) + { + /* 读取数据. */ + data_len = recvfrom(dau_ctrl.fd, dau_ctrl.recv_buf, DAU_PKT_LEN, 0, (struct sockaddr*)&server, &server_len); + if (data_len <= 0) + { + if (!dau_ctrl.recv_err_cnt) + { + log_err(LOG_DAU, "DAU receive ERRORR!"); + } + dau_ctrl.recv_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU recvfrom ERROR\r\n"); + continue; + } + + dau_ctrl.recv_cnt++; + /* 申请报文空间, 8是报文总长度. */ + pkt = XMALLOC_Q(MTYPE_DAU, data_len); + if (!pkt) + { + continue; + } + + /* 封装消息. */ + memcpy(pkt, dau_ctrl.recv_buf, data_len); + recv_msg.ip_addr = server.sin_addr.s_addr; + recv_msg.recv_len = data_len; + recv_msg.data = pkt; + + /* 发送消息 */ +#ifdef DAU_IPC_FIFO + if (write(fd, (char*)(&recv_msg), sizeof(dau_recv_msg_data_t)) != sizeof(dau_recv_msg_data_t)) + { + DBG(DBG_M_PD_DAU_ERR, "DAU write ERROR return %s!\r\n", safe_strerror(errno)); + XFREE(MTYPE_DAU, pkt); + continue; + } +#endif + /* DAU 收报同步调试打印. */ +// temp = (dau_pkt_t*)pkt; +// if (temp->func == 0x81 && temp->addr == 0) +// { +// printf("%x ", *(uint32_t*)(&temp->data[0])); +// temp1++; +// if (temp1 >= 20) +// { +// temp1 = 0; +// printf("\r\n"); +// } +// } + if (fifo_write(dau_ctrl.recv_fifo_id, (void*)(&recv_msg), sizeof(dau_recv_msg_data_t)) != sizeof(dau_recv_msg_data_t)) + { + if (!dau_ctrl.recv_err_cnt) + { + log_err(LOG_DAU, "DAU fifo write ERROR!"); + } + dau_ctrl.recv_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU fifo write ERROR\r\n"); + XFREE(MTYPE_DAU, pkt); + continue; + } + + } + + return NULL; +} + +/* DAU 报文处理函数. */ +void *_dau_data_handle(void *arg) +{ + dau_recv_msg_data_t *recv_msg = NULL; + +#ifdef DAU_IPC_FIFO + int fd = 0; + + fd = open(DAU_RECV_FIFO, O_RDONLY); + if (fd == -1) + { + log_err(LOG_DAU, "Open read fifo file " DAU_RECV_FIFO " error!"); + return NULL; + } +#endif + + while(1) + { + /* 读取数据. */ +#ifdef DAU_IPC_FIFO + if (read(fd, (char*)(&recv_msg), sizeof(dau_recv_msg_data_t)) != sizeof(dau_recv_msg_data_t)) + { + DBG(DBG_M_PD_DAU_ERR, "ERROR at msgrcv return %s\r\n!", safe_strerror(errno)); + continue; + } +#endif + if (fifo_read(dau_ctrl.recv_fifo_id, (void**)&recv_msg) != 0) + { + if (!dau_ctrl.data_err_cnt) + { + log_err(LOG_DAU, "DAU%d fifo read ERRORR!", dau_ctrl.recv_fifo_id); + } + dau_ctrl.data_err_cnt++; + DBG_Q(DBG_M_PD_DAU_ERR, "DAU%d fifo read ERRORR!\r\n", dau_ctrl.recv_fifo_id); + continue; + } + + /* 数据处理. */ + _dau_data_process(recv_msg->ip_addr, (dau_pkt_t*)recv_msg->data, recv_msg->recv_len); + + /* 释放数据内存, 注意一定要在 fifo_push 之前调用, 因为 fifo_push 后 recv_msg 已被释放. */ + XFREE(MTYPE_DAU, recv_msg->data); + fifo_push(dau_ctrl.recv_fifo_id); + } + + return NULL; +} + +/* 配置保存函数. */ +int _dau_config_save(vty_t* vty) +{ + dau_t *dau_node = NULL; + int16_t i = 0; + uint8_t unit = 0; + uint8_t mode = 0; + + /* 遍历所有 DAU. */ + for (unit = 0; unit < PD_DAU_SUM; unit++) + { + dau_node = dau[unit]; + + if (!dau_node) + { + continue; + } + + if (!dau_node->is_valid) + { + continue; + } + + vty_out(vty, "dau %d port-num %d%s", unit + 1, dau_node->port_num, VTY_NEWLINE); + i++; + + mode = (dau_node->reg.GCR & DAU_GCR_MODE_Msk) >> DAU_GCR_MODE_Pos; + vty_out(vty, "dau %d mode %s%s", unit + 1, dau_mode_str[mode], VTY_NEWLINE); + i++; + } + + return i; +} + +/* config模式配置保存函数: vty -- 相应的终端 */ +int _dau_port_config_save(vty_t *vty, uint8_t unit, uint8_t port) +{ + dau_t *dau_node = NULL; + uint16_t field = 0; + char str[5] ={0}; + + dau_node = dau[unit]; + + field = (dau_node->port_reg[port].CR & DAU_CR_PT_Msk) >> DAU_CR_PT_Pos; + vty_out(vty, " port-type %s%s", dau_port_type_str[field], VTY_NEWLINE); + + field = (dau_node->port_reg[port].CR & DAU_CR_SR_Msk) >> DAU_CR_SR_Pos; + vty_out(vty, " sample-rate %d%s", DAU_ADC_RATE / field, VTY_NEWLINE); + + field = pd_config.port_config[unit][port].config.filter; + switch(field) + { + case CSG_FILTER_TYPE_HF: + snprintf(str, 5, "hf"); + break; + case CSG_FILTER_TYPE_LF: + snprintf(str, 5, "lf"); + break; + case CSG_FILTER_TYPE_FR: + snprintf(str, 5, "fr"); + break; + case CSG_FILTER_TYPE_FF: + snprintf(str, 5, "ff"); + break; + default: + snprintf(str, 5, "none"); + break; + } + vty_out(vty, " filtrate-type %s%s", str, VTY_NEWLINE); + + return E_NONE; +} + +/* DAU 内部公共部分初始化. */ +int32_t _dau_handle_init_common(void) +{ + struct sockaddr_in server; + struct sched_param param; + pthread_attr_t attr; + pthread_t pid; + int32_t rv = 0; + int fd = 0; + + /* 创建协议 socket 与 DAU 通讯. */ + if (0 == dau_ctrl.fd) + { + /* 创建socket */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + log_err(LOG_DAU, "ERROR at socket create return %s!", safe_strerror(errno)); + return E_SYS_CALL; + } + + /* 绑定端口 */ + bzero(&server, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = htonl(INADDR_ANY); + server.sin_port = htons(7000); + if(bind(fd, (struct sockaddr*)&server, sizeof(server)) < 0) + { + log_err(LOG_DAU, "ERROR at socket bind return %s!", safe_strerror(errno)); + close(fd); + return E_SYS_CALL; + } + + /* 保存 socket. */ + dau_ctrl.fd = fd; + } + + /* 创建消息队列, 用于将 DAU 收到的数据发送到处理线程, 这里使用队列是为了缓冲数据. */ +#ifdef DAU_IPC_FIFO + if (-1 == access(DAU_RECV_FIFO, F_OK)) + { + if (mkfifo(DAU_RECV_FIFO, 0666) < 0) + { + log_err(LOG_DAU, "Cannot create fifo file " DAU_RECV_FIFO "!"); + return E_SYS_CALL; + } + } + else + { + remove(DAU_RECV_FIFO); + if (mkfifo(DAU_RECV_FIFO, 0666) < 0) + { + log_err(LOG_DAU, "Cannot create fifo file " DAU_RECV_FIFO "!"); + return E_SYS_CALL; + } + } +#endif + + dau_ctrl.recv_fifo_id = fifo_create(DAU_RECV_FIFO, 128); + if (dau_ctrl.recv_fifo_id < 0) + { + log_err(LOG_DAU, "Open fifo " DAU_RECV_FIFO " error!"); + return E_NONE; + } + + /* 初始化 DAU 报文处理线程. */ + /* 配置线程RR调度, 优先级79 */ + pthread_attr_init(&attr); + param.sched_priority = 79; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _dau_data_handle, NULL); + if (rv != 0) + { + log_err(LOG_DAU, "PD can't create DAU data pthread %d!", rv); + return E_SYS_CALL; + } + else + { + thread_m_add("PD_DAU_DATA", pid); + } + pthread_attr_destroy(&attr); + + /* 初始化 DAU 收包线程. */ + /* 配置线程RR调度, 优先级80 */ + pthread_attr_init(&attr); + param.sched_priority = 80; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _dau_recv_handle, NULL); + if (rv != 0) + { + log_err(LOG_DAU, "DAU can't create recv pthread %d!", rv); + return E_SYS_CALL; + } + else + { + thread_m_add("PD_DAU_RECV", pid); + } + pthread_attr_destroy(&attr); + + /* 复位 fpga. */ + _dau_reset(); + + return E_NONE; +} + +/* Interface functions -------------------------------------------------------*/ +/* DAU 模块初始化. */ +int32_t dau_handle_init(void) +{ + int32_t rv = 0; + +#if 0 + /* 初始化 DAU 模块. */ + LD_E_RETURN(DBG_M_PD_DAU_ERR, _dau_handle_init_common()); +#endif + + /* 注册命令行 */ + cmd_install_element(CONFIG_NODE, &dau_add_cmd); + cmd_install_element(CONFIG_NODE, &no_dau_add_cmd); + cmd_install_element(CONFIG_NODE, &dau_master_slave_mode_cmd); + + cmd_install_element(PORT_NODE, &dau_port_type_cmd); + cmd_install_element(PORT_NODE, &dau_sample_rate_cmd); + cmd_install_element(PORT_NODE, &dau_filter_type_cmd); + cmd_install_element(PORT_NODE, &dau_port_sync_cmd); + + cmd_install_element(COMMON_NODE, &show_dau_status_all_cmd); + cmd_install_element(COMMON_NODE, &show_dau_adj_all_cmd); + cmd_install_element(COMMON_NODE, &show_dau_reg_all_cmd); + cmd_install_element(COMMON_NODE, &show_dau_reg_port_cmd); + +#if 0 + /* 注册配置保存函数 */ + rv = cmd_config_node_config_register(CONFIG_PRI_DAU, _dau_config_save); + if (rv != E_NONE) + { + log_err(LOG_DAU, "Command save register ERROR %d!", rv); + return rv; + } + + rv = pd_port_cmd_config_register(PD_PORT_CMD_PRI_DAU, _dau_port_config_save); + if (rv != E_NONE) + { + log_err(LOG_DAU, "DAU port command save register ERROR %d!", rv); + return rv; + } +#endif + return E_NONE; +} + +/* 将 unit port 转换成 vport. */ +int32_t dau_port_to_vport(uint8_t unit, uint8_t port) +{ + uint8_t i = 0; + uint8_t vport = 0; + + if (unit >= PD_DAU_SUM) + { + return E_BAD_PARAM; + } + + for(i = 0; i < unit; i++) + { + if (!dau[i]) + { + continue; + } + + vport += dau[i]->port_num; + } + + if (!dau[i]) + { + return E_BAD_PARAM; + } + + if (port >= dau[i]->port_num) + { + return E_BAD_PARAM; + } + + return vport += port + 1; +} + +/* 将 unit port 转换成 vport. */ +int32_t dau_vport_to_port(uint8_t vport, uint8_t *unit, uint8_t *port) +{ + uint8_t i = 0; + + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + if (vport <= dau[i]->port_num + && vport != 0) + { + *unit = i; + *port = vport - 1; + return E_NONE; + } + else + { + vport -= dau[i]->port_num; + } + } + + return E_BAD_PARAM; +} + +/* 端口配置刷新对外接口. */ +int32_t dau_port_config_set(uint8_t unit, uint8_t port) +{ + dau_t *dau_node = NULL; + uint16_t temp = 0; + + if (unit >= PD_DAU_SUM) + { + return E_BAD_PARAM; + } + + if (NULL == (dau_node = dau[unit])) + { + return E_BAD_PARAM; + } + + if (port >= dau_node->port_num) + { + return E_BAD_PARAM; + } + + temp = pd_config.port_config[unit][port].config.sample_rate / 1000000; + temp = (temp << DAU_CR_SR_Pos); + MODIFY_REG(dau_node->port_reg[port].CR, DAU_CR_SR_Msk, temp); + + temp = pd_config.port_config[unit][port].filter_cfg; + switch(temp) + { + case CSG_FILTER_TYPE_FF: + temp = PD_FILTER_TYPE_FF; + break; + case CSG_FILTER_TYPE_FR: + temp = PD_FILTER_TYPE_FR; + break; + case CSG_FILTER_TYPE_LF: + temp = PD_FILTER_TYPE_LF; + break; + case CSG_FILTER_TYPE_HF: + temp = PD_FILTER_TYPE_HF; + break; + default: + return E_BAD_PARAM; + } + temp = (temp << DAU_FTR_FT_Pos); + MODIFY_REG(dau_node->port_reg[port].FTR, DAU_FTR_FT_Msk, temp); + + return E_NONE; +} + +/* 端口类型设置. */ +int32_t dau_port_type_set(uint8_t unit, uint8_t port, uint8_t type) +{ + dau_t *dau_node = NULL; + uint16_t temp = 0; + + if (unit >= PD_DAU_SUM) + { + return E_BAD_PARAM; + } + + if (NULL == (dau_node = dau[unit])) + { + return E_BAD_PARAM; + } + + if (port >= dau_node->port_num) + { + return E_BAD_PARAM; + } + + temp = (type << DAU_CR_PT_Pos); + MODIFY_REG(dau_node->port_reg[port].CR, DAU_CR_PT_Msk, temp); + + if (PD_PORT_TYPE_UHF == type) + { + dau_node->port_func[port] = &gis_func; + } + else + { + dau_node->port_func[port] = &gis_func; + } + + return E_NONE; +} + +/* 获取工频周期. */ +uint8_t dau_power_frequency_state_get(void) +{ + dau_t *dau_node = NULL; + + if (NULL == (dau_node = dau[0])) + { + return 0; + } + + return (dau_node->reg.GSR & DAU_GSR_SYNC_Msk) >> DAU_GSR_SYNC_Pos; +} + +/* 获取工频周期. */ +float dau_power_frequency_get(void) +{ + dau_t *dau_node = NULL; + float temp = 100000000.00; + + if (NULL == (dau_node = dau[0])) + { + return 0; + } + + return dau_node->reg.GPFPCCR ? (temp / (dau_node->reg.GPFPCCR / 50)) : 0; +} + +/* 获取工频周期. */ +uint8_t dau_connect_get(void) +{ + uint8_t unit = 0; + uint8_t state = 0; + + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + if (dau[unit]->is_connect) + { + state |= 1 << unit; + } + } + + return state; +} + +/* 获取端口状态寄存器. */ +void* dau_port_state_get(void *arg) +{ + uint8_t unit = 0; + + if (!dau_ctrl.is_update && debug_ctrl.is_manual_col) + { + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + _dau_reg_read(dau[unit]->unit, DAU_REG_PORT_ADDR_MSRR, PD_DAU_PORT_SUM << 1); + } + } + + return NULL; +} + +/* 配置全局控制寄存器. */ +int32_t dau_reg_global_write(uint8_t unit) +{ + dau_t *dau_node = NULL; + + DBG(DBG_M_PD_DAU, "Entry: unit %d\r\n", unit); + + if (unit >= PD_DAU_SUM) + { + return E_BAD_PARAM; + } + + if (NULL == (dau_node = dau[unit])) + { + return E_BAD_PARAM; + } + + LD_E_RETURN(DBG_M_PD_DAU_ERR, _dau_reg_write(unit, DAU_REG_ADDR_GCR, 1, &dau_node->reg.GCR)); + + DBG(DBG_M_PD_DAU, "Leave\r\n"); + + return E_NONE; +} + +/* 获取端口状态寄存器. */ +int32_t dau_reg_port_write(uint8_t unit, uint8_t port) +{ + dau_t *dau_node = NULL; + uint16_t addr = DAU_REG_PORT_ADDR_GET(port); + + DBG(DBG_M_PD_DAU, "Entry: unit %d port %d\r\n", unit, port); + + if (unit >= PD_DAU_SUM) + { + return E_BAD_PARAM; + } + + if (NULL == (dau_node = dau[unit])) + { + return E_BAD_PARAM; + } + + if (port >= dau_node->port_num) + { + return E_BAD_PARAM; + } + + LD_E_RETURN(DBG_M_PD_DAU_ERR, _dau_reg_write(unit, addr, sizeof(dau_port_reg_t) >> 1, &dau_node->port_reg[port].CR)); + + DBG(DBG_M_PD_DAU, "Leave\r\n"); + + return E_NONE; +} + +/* 获取端口状态寄存器. */ +int32_t dau_param_save(uint8_t unit) +{ + dau_send_msg_t send_msg; + dau_pkt_t *pkt = NULL; + dau_t *dau_node = dau[unit]; + + if (unit >= PD_DAU_SUM) + { + return E_BAD_PARAM; + } + + if (NULL == (dau_node = dau[unit])) + { + return E_BAD_PARAM; + } + + if (dau_ctrl.is_update) + { + return E_NONE; + } + + if (!dau_node->is_valid) + { + return E_NONE; + } + + /* 申请报文空间 */ + pkt = XMALLOC_Q(MTYPE_DAU, 8); + if (!pkt) + { + return E_MEM; + } + + /* 组装报文. */ + pkt->slave_id = 0x0B; + pkt->func = DAU_PKT_CMD_SAV; + pkt->addr = 0; + pkt->len = 0; + pkt->data[0] = crc16_modbus((uint8_t*)pkt, 6); + + /* 封装消息. */ + memset(&send_msg, 0, sizeof(dau_send_msg_t)); + send_msg.type = dau_node->msg_send_type; + send_msg.data.send_type = DAU_SEND_TYPE_REG_WR; + send_msg.data.send_len = 8; + send_msg.data.data = pkt; + + /* 发送消息. */ + if ((msgsnd(recv_qid, &send_msg, sizeof(dau_send_msg_data_t), 0)) < 0) + { + DBG(DBG_M_PD_DAU_ERR, "DAU msgsnd ERROR return %s!\r\n", safe_strerror(errno)); + XFREE(MTYPE_DAU, pkt); + return E_SYS_CALL; + } + + return E_NONE; +} + +void dau_utc_enable(void) +{ + uint8_t i = 0; + uint16_t dau_bitmap = 0; + + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + BITMAP_SET(dau_bitmap, i); + } + + cpld_write(0x0c, 1, &dau_bitmap); +} + +void dau_utc_disable(void) +{ + uint16_t temp = 0; + + cpld_write(0x0c, 1, &temp); +} + +void dau_shutdown(void) +{ + uint8_t i = 0; + + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + dau[i]->is_valid = FALSE; + } + + GPIO_DAU1_POW_OFF; + GPIO_DAU2_POW_OFF; + + dau_utc_disable(); + sleep(1); +} + +int32_t dau_start(void) +{ + uint8_t i = 0; + uint8_t j = 0; + uint8_t cnt = 0; + + /* DAU 上电. */ + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + if (0 == i) + { + GPIO_DAU1_POW_ON; + } + else if(1 == i) + { + GPIO_DAU2_POW_ON; + } + } + sleep(4); + + /* 设置为有效. */ + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + dau[i]->is_valid = TRUE; + } + + for(i = 0; i < PD_DAU_SUM; i++) + { + if (!dau[i]) + { + continue; + } + + /* 读取写入全局状态寄存器. */ + _dau_reg_global_read(i); + dau_reg_global_write(i); + + /* 读通道校准系数. */ + LD_E_RETURN(DBG_M_PD_DAU_ERR, _dau_param_get(i)); + + /* 将端口配置同步到 DAU. */ + for (j = 0; j < dau[i]->port_num; j++) + { + dau_reg_port_write(i, j); + } + + /* 等待工频同步成功. */ + while(1) + { + cnt++; + if ((dau[i]->reg.GSR & DAU_GSR_SYNC_Msk) >> DAU_GSR_SYNC_Pos) + { + break; + } + + if (0 == (cnt & 0x7)) + { + DBG(DBG_M_PD_DAU_ERR, "DAU%d can't sync!\r\n", i); + break; + } + sleep(1); + _dau_reg_global_read(i); + } + } + + /* 使能同步采样. */ + dau_utc_enable(); + + return E_NONE; +} + +/* DAU 远程升级处理, 升级所有 DAU. */ +int32_t dau_update(void) +{ + dau_send_msg_t send_msg; + dau_pkt_t *pkt = NULL; + dau_t *dau_node = NULL; + uint8_t unit = 0; + uint8_t cnt = 0; + uint16_t data_len = 1032; // 8byte 数据头 + 1024byte 数据. + int32_t rv = E_ERROR; + + /* 更新标志, 设置为升级状态 */ + if (dau_ctrl.is_update) + { + return E_TIMEOUT; + } + dau_ctrl.is_update = TRUE; + + /* 关闭 UTC 同步采集. */ + dau_utc_disable(); + + /* 遍历 DAU, 串行升级. */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + /* 检查 DAU 是否存在. */ + dau_node = dau[unit]; + if (!dau_node) + { + continue; + } + + if (!dau_node->is_valid) + { + continue; + } + + /* 初始化状态. */ + dau_ctrl.update_ret = DAU_UPD_PROCESS; + dau_ctrl.update_idx = 0; + + /* 申请报文空间. */ + pkt = XMALLOC_Q(MTYPE_DAU, data_len); + if (!pkt) + { + rv = E_MEM; + break; + } + + /* 封装消息. */ + memset(&send_msg, 0, sizeof(dau_send_msg_t)); + send_msg.type = dau_node->msg_send_type; + send_msg.data.send_type = DAU_SEND_TYPE_UPDATE; + send_msg.data.send_len = data_len; + send_msg.data.data = pkt; + + /* 发送消息 */ + if ((msgsnd(recv_qid, &send_msg, sizeof(dau_send_msg_data_t), 0)) < 0) + { + DBG(DBG_M_PD_DAU_ERR, "DAU msgsnd ERROR return %s!\r\n", safe_strerror(errno)); + XFREE(MTYPE_DAU, pkt); + break; + } + log_notice(LOG_DAU, "DAU%d update start!", unit); + + /* 等待升级完成. */ + while(1) + { + sleep(1); + if (dau_ctrl.update_ret < 0) + { + dau_ctrl.is_update = FALSE; + dau_utc_enable(); + return E_ERROR; + } + else if(DAU_UPD_OK == dau_ctrl.update_ret) + { + log_notice(LOG_DAU, "DAU%d update OK!", unit); + rv = E_NONE; + break; + } + + cnt++; + if (cnt >= 30) + { + dau_ctrl.is_update = FALSE; + dau_utc_enable(); + return E_TIMEOUT; + } + } + } + + dau_ctrl.is_update = FALSE; + dau_utc_enable(); + return rv; +} + +/* 显示 DAU 状态. */ +void dau_show(uint8_t unit) +{ + dau_t *dau_node = NULL; + + dau_node = dau[unit]; + if (!dau_node) + { + return; + } + + printh("DAU %d: \r\n", unit + 1); + printh(" Version: %d.%d\r\n", (dau_node->reg.GSVR & DAU_GSVR_PRIMARY_Msk) >> DAU_GSVR_PRIMARY_Pos, + (dau_node->reg.GSVR & DAU_GSVR_SECONDARY_Msk) >> DAU_GSVR_SECONDARY_Pos); + printh(" Mode: %s\r\n", (dau_node->reg.GSR & DAU_GSR_MODE_Msk) >> DAU_GSR_MODE_Pos ? "master" : "slave"); + printh(" Synchronization: %s\r\n", (dau_node->reg.GSR & DAU_GSR_SYNC_Msk) >> DAU_GSR_SYNC_Pos ? "valid" : "invalid"); + printh(" Power frequency period: %.2f\r\n", dau_power_frequency_get()); + printh(" Power frequency count: %d\r\n", dau_node->reg.GPFPCR); + printh(" Clock ns: %d\r\n", dau_node->reg.GNSCR); + printh(" Valid: %d\r\n", dau_node->is_valid); + printh(" connected: %d\r\n\n", dau_node->is_connect); + + return; +} + +/* 显示 DAU 校准系数. */ +void dau_show_adj(uint8_t unit) +{ + dau_t *dau_node = NULL; + dau_port_reg_t *reg = NULL; + uint8_t port = 0; + + dau_node = dau[unit]; + if (!dau_node) + { + return; + } + + printh("DAU %d: \r\n", unit + 1); + for(port = 0; port < dau_node->port_num; port++) + { + reg = &dau_node->port_reg[port]; + printh("%d | %05d %05d %05d | %05d %05d %05d | %05d %05d %05d |", port + 1, reg->AFAR[0], reg->AFBR[0], reg->ASPR[0], + reg->AFAR[1], reg->AFBR[1], reg->ASPR[1], reg->AFAR[2], reg->AFBR[2], reg->ASPR[2]); + printh(" %05d %05d %05d | %05d %05d %05d |\r\n", reg->AFAR[3], reg->AFBR[3], reg->ASPR[3], reg->AFAR[4], reg->AFBR[4], reg->ASPR[4]); + } + printh("\r\n"); +} + +/* 显示 DAU 全局寄存器. */ +void dau_show_reg(uint8_t unit) +{ + dau_t *dau_node = NULL; + uint16_t *reg = NULL; + uint8_t num = sizeof(dau_reg_t) >> 1; + uint8_t row = 0; + uint8_t col = 0; + uint8_t i = 0; + uint8_t j = 0; + + dau_node = dau[unit]; + if (!dau_node) + { + return; + } + + row = num >> 4; + if (num & 0x0f) + { + row++; + } + + printh("DAU %d: \r\n", unit + 1); + for(i = 0; i < row; i++) + { + col = num - (i << 4); + col = col > 16 ? 16 : col; + printh(" "); + for(j = 0; j < col; j++) + { + printh("%04x ", (i << 4) + j); + } + + reg = &dau_node->reg.GSCR; + reg += (i << 4); + printh("\r\nC "); + for(j = 0; j < col; j++) + { + printh("%04x ", reg[j]); + } + + reg = &dau_node->reg_dau.GSCR; + reg += (i << 4); + printh("\r\nD "); + for(j = 0; j < col; j++) + { + printh("%04x ", reg[j]); + } + printh("\r\n\n"); + } +} + +/* 显示 DAU 端口寄存器. */ +void dau_show_reg_port(uint8_t unit, uint8_t port) +{ + dau_t *dau_node = NULL; + uint16_t *reg = NULL; + uint8_t num = sizeof(dau_port_reg_t) >> 1; + uint8_t row = 0; + uint8_t col = 0; + uint8_t i = 0; + uint8_t j = 0; + + dau_node = dau[unit]; + if (!dau_node) + { + return; + } + + _dau_reg_port_read(unit, port); + usleep(100000); + + row = num >> 4; + if (num & 0x0f) + { + row++; + } + + printh("DAU %d port %d: \r\n", unit + 1, port + 1); + for(i = 0; i < row; i++) + { + col = num - (i << 4); + col = col > 16 ? 16 : col; + printh(" "); + for(j = 0; j < col; j++) + { + printh("%04x ", (i << 4) + j); + } + + reg = &dau_node->port_reg[port].CR; + reg += (i << 4); + printh("\r\nC "); + for(j = 0; j < col; j++) + { + printh("%04x ", reg[j]); + } + + reg = &dau_node->port_reg_dau[port].CR; + reg += (i << 4); + printh("\r\nD "); + for(j = 0; j < col; j++) + { + printh("%04x ", reg[j]); + } + printh("\r\n\n"); + } +} + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/lib/a_process/pd_dbg.c b/app/lib/a_process/pd_dbg.c new file mode 100755 index 0000000..86958d8 --- /dev/null +++ b/app/lib/a_process/pd_dbg.c @@ -0,0 +1,1214 @@ +/***************************************************************************** + * file lib/process/pd_dbg.c + * author YuLiang + * version 1.0.0 + * date 01-June-2023 + * brief This file provides all the debug server related operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef CFG_DEV_TYPE_LAND_PD +/* 标准C库头文件. */ +#include +#include +#include +#include +#include +#include + +/* 用户代码头文件. */ +#include "cJSON.h" +#include "process.h" +#include "mtimer.h" +#include "pd_dbg.h" +#include "pd_csg.h" +#include "pd_dau.h" + +/* Private define ------------------------------------------------------------*/ +#define DEBUG_HEAD 0x55AA +#define DEBUG_TAIL 0x5AA5 + +#define DEBUG_CMD_LEN 10 * 1024 * 1024 + +#define DEBUG_CMU_FILE "PDMonitor" +#define DEBUG_CMU_FILE_BAK "PDMonitor.bak" + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +debug_ctrl_t debug_ctrl; +static char cjson_buf[DEBUG_BUG_SIZE]; +static char buf_cmd[DEBUG_CMD_LEN]; + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +void _debug_pkt_head_int(debug_pkt_head_t *head, uint16_t cmd, uint32_t len) +{ + head->head = DEBUG_HEAD; + head->cmd = cmd; + head->len = len; +} + +void _debug_save_fatcory_param(cJSON *root) +{ + cJSON *param = NULL; + struct in_addr addr = {0}; + dau_t *dau_node = NULL; + uint16_t len = 0; + uint16_t field = 0; + uint8_t i = 0; + uint8_t j = 0; + uint8_t cnt = 0; + + param = cJSON_AddObjectToObject(root, "EquipmentFactoryParam"); + if (NULL == param) + { + DBG(DBG_M_DEBUG, "Add json object error!\r\n"); + return; + } + + cJSON_AddStringToObject(param, "DeviceNum", host.name); + snprintf(cjson_buf, DEBUG_CJSON_BUG_SIZE, "%s", device_info.dev_type); + cJSON_AddStringToObject(param, "EquipmentType", cjson_buf); + cJSON_AddNumberToObject(param, "DateOfProduction", device_info.factory_date); + cJSON_AddNumberToObject(param, "DeploymentDate", device_info.deployment_date); + cJSON_AddStringToObject(param, "LocalIP1", device_info.host_ip); + cJSON_AddStringToObject(param, "LocalIP1_V6", ""); + cJSON_AddStringToObject(param, "LocalMacAddress1", ""); + cJSON_AddStringToObject(param, "LocalIP2", ""); + cJSON_AddStringToObject(param, "LocalIP2_V6", ""); + cJSON_AddStringToObject(param, "LocalMacAddress2", ""); + addr.s_addr = csg.server_ip; + cJSON_AddStringToObject(param, "ServerIP1", inet_ntoa(addr)); + cJSON_AddStringToObject(param, "ServerIP1_V6", ""); + cJSON_AddStringToObject(param, "ServerIP2", ""); + cJSON_AddStringToObject(param, "ServerIP2_V6",""); + cJSON_AddNumberToObject(param,"ServerIP1_Port", csg.server_port); + cJSON_AddNumberToObject(param,"ServerIP2_Port", 0); + + for(i = 0; i < PD_DAU_SUM; i++) + { + dau_node = dau[i]; + if (!dau_node) + { + continue; + } + + for(j = 0; j < dau_node->port_num; j++) + { + field = (dau_node->port_reg[j].CR & DAU_CR_PT_Msk) >> DAU_CR_PT_Pos; + len += snprintf(cjson_buf + len, DEBUG_CJSON_BUG_SIZE - len, "%02x", field); + cnt++; + } + } + + for(i = cnt; i < PD_PORT_SUM; i++) + { + len += snprintf(cjson_buf + len, DEBUG_CJSON_BUG_SIZE - len, "00"); + } + + cJSON_AddStringToObject(param, "ChannelType", cjson_buf); + + return; +} + +void _debug_save_user_param(cJSON *root) +{ + cJSON *param = NULL; + + param = cJSON_AddObjectToObject(root, "EquipmentUserParam"); + if (NULL == param) + { + DBG(DBG_M_DEBUG, "Add json object error!\r\n"); + return; + } + + cJSON_AddNumberToObject(param, "SystemFrequency", pd_config.config.power_frequency); + cJSON_AddNumberToObject(param, "SyncMode", pd_config.config.sync_mode); + cJSON_AddNumberToObject(param, "TimingSyncMode", pd_config.config.TimingSyncMode); + cJSON_AddNumberToObject(param, "DataAcquisitionCycle", pd_config.config.real_period); + cJSON_AddNumberToObject(param, "TrendDataReportCycle", pd_config.config.trend_period); + cJSON_AddNumberToObject(param, "HeartbeatCycle", pd_config.config.heartbeat_period); + cJSON_AddNumberToObject(param, "NumberWindowsOfPhase", pd_config.config.NumberWindowsOfPhase); + cJSON_AddNumberToObject(param, "SyncTime", pd_config.config.SyncTime); + cJSON_AddNumberToObject(param, "AlarmPeriod", pd_config.config.AlarmPeriod); + cJSON_AddNumberToObject(param, "TimePrpsStrategy", pd_config.config.TimePrpsStrategy); + cJSON_AddNumberToObject(param, "StorageTrent", pd_config.config.storage_trend); + cJSON_AddNumberToObject(param, "StorageAlarm", pd_config.config.StorageAlarm); + cJSON_AddNumberToObject(param, "StorageRun", pd_config.config.StorageRun); + cJSON_AddNumberToObject(param, "StorageFtPRPS", pd_config.config.storage_real); + cJSON_AddNumberToObject(param, "LimitEventStorageTime", pd_config.config.limit_event_time); + cJSON_AddNumberToObject(param, "LimitEventStorageCount", pd_config.config.limit_event_cnt); + cJSON_AddNumberToObject(param, "LimitEventStorageSpace", pd_config.config.limit_event_interval); + cJSON_AddNumberToObject(param, "prpsSaveEnable", pd_config.config.is_prps_save); + + return; +} + +void _debug_save_port_param(cJSON *root) +{ + cJSON *port_array = NULL; + cJSON *param = NULL; + pd_port_config_old_t *config = NULL; + uint8_t unit = 0; + uint8_t port = 0; + uint8_t i = 0; + + port_array = cJSON_AddArrayToObject(root, "SensorConfigParam"); + if (NULL == port_array) + { + DBG(DBG_M_DEBUG, "Add json array error!\r\n"); + return; + } + + for(i = 1; i <= PD_PORT_SUM; i++) + { + if (dau_vport_to_port(i, &unit, &port) != E_NONE) + { + break; + } + + config = &pd_config.port_config[unit][port].config; + + param = cJSON_CreateObject(); + if (!param) + { + DBG(DBG_M_DEBUG, "Create json error!\r\n"); + return; + } + + cJSON_AddNumberToObject(param,"ChannelNumber", config->vport); + cJSON_AddNumberToObject(param,"FilterType", config->filter); + cJSON_AddNumberToObject(param,"SensorType", config->sensor_type); + cJSON_AddNumberToObject(param,"PhaseSequence", config->phase_sequence); + cJSON_AddNumberToObject(param,"NearSensor1_Number", config->NearSensor1_Number); + cJSON_AddNumberToObject(param,"NearSensor1_Distance", config->NearSensor1_Distance); + cJSON_AddNumberToObject(param,"Signal1_AttenuationRatio", config->Signal1_AttenuationRatio); + cJSON_AddNumberToObject(param,"NearSensor2_Number", config->NearSensor2_Number); + cJSON_AddNumberToObject(param,"NearSensor2_Distance", config->NearSensor2_Distance); + cJSON_AddNumberToObject(param,"Signal2_AttenuationRatio", config->Signal2_AttenuationRatio); + cJSON_AddNumberToObject(param,"NearSensor3_Number", config->NearSensor3_Number); + cJSON_AddNumberToObject(param,"NearSensor3_Distance", config->NearSensor3_Distance); + cJSON_AddNumberToObject(param,"Signal3_AttenuationRatio", config->Signal3_AttenuationRatio); + cJSON_AddNumberToObject(param,"NearSensor4_Number", config->NearSensor4_Number); + cJSON_AddNumberToObject(param,"NearSensor4_Distance", config->NearSensor4_Distance); + cJSON_AddNumberToObject(param,"Signal4_AttenuationRatio", config->Signal4_AttenuationRatio); + cJSON_AddNumberToObject(param,"EventTriggerThreshold", config->event_threshold); + cJSON_AddNumberToObject(param,"EventTriggerCounter", config->event_counter); + cJSON_AddNumberToObject(param,"EnvironmentalNoiseThreshold", config->env_noise); + cJSON_AddNumberToObject(param,"NoiseReductionLevel", config->noise_reduction); + cJSON_AddNumberToObject(param,"UpperLimitOfDischargeAmplitude", config->UpperLimitOfDischargeAmplitude); + cJSON_AddNumberToObject(param,"WaveTriggerThreshold", config->WaveTriggerThreshold); + cJSON_AddNumberToObject(param,"PrescalerSampleValue", config->PrescalerSampleValue); + cJSON_AddNumberToObject(param,"SamplePointsTotalTriggered", config->SamplePointsTotalTriggered); + cJSON_AddNumberToObject(param,"RecordStartAdr", config->RecordStartAdr); + cJSON_AddNumberToObject(param,"RecordStopAdr", config->RecordStopAdr); + cJSON_AddNumberToObject(param,"SampleRate", config->sample_rate); + cJSON_AddNumberToObject(param,"BeforeTriggerTime", config->BeforeTriggerTime); + cJSON_AddNumberToObject(param,"AfterTriggerTime", config->AfterTriggerTime); + cJSON_AddNumberToObject(param,"StorageEvent", config->storage_event); + cJSON_AddNumberToObject(param,"StorageAEWave", config->storage_wave); + + cJSON_AddItemToArray(port_array, param); + } + + return; +} + +cJSON* _debug_save_cfg_to_json(void) +{ + cJSON *root = NULL; + + root = cJSON_CreateObject(); + if (!root) + { + DBG(DBG_M_DEBUG, "Create json error!\r\n"); + return NULL; + } + + _debug_save_fatcory_param(root); + _debug_save_user_param(root); + _debug_save_port_param(root); + + return root; +} + +/* 调试工具修改本地ip 设备ID 服务器地址时需要重启。*/ +void _debug_parse_fatcory_param(cJSON *root, unsigned int *changbitmap, BOOT_MSG *boottype) +{ + struct sockaddr_in addr = {0}; + cJSON *param = cJSON_GetObjectItem(root, "EquipmentFactoryParam"); + char *str = NULL; + uint8_t unit = 0; + uint8_t port = 0; + uint8_t i = 0; + uint8_t type = 0; + + if (!param) + { + return; + } + + str = cJSON_GetStringValue(cJSON_GetObjectItem(param, "EquipmentType")); + if (str) + { + if (strncmp(str, device_info.dev_type, sizeof(device_info.dev_type)) != 0) + { + snprintf(device_info.dev_type, FILE_NAME_LEN, "%s", str); + *changbitmap |= BITMAP_SAVE_FILE; + } + } + + str = cJSON_GetStringValue(cJSON_GetObjectItem(param, "DeviceNum")); + if (str) + { + if (strncmp(str, host.name, sizeof(host.name)) != 0) + { + snprintf(host.name, FILE_NAME_LEN, "%s", str); + *changbitmap |= BITMAP_SAVE_FILE; + *boottype = BOOT_LOCAL_HOST_NAME_CHANGE; + } + } + + device_info.factory_date = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "DateOfProduction")); + device_info.deployment_date = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "DeploymentDate")); + + str = cJSON_GetStringValue(cJSON_GetObjectItem(param, "LocalIP1")); + if (str) + { + if (strncmp(str, device_info.host_ip, sizeof(device_info.host_ip)) != 0) + { + snprintf(device_info.host_ip, INET_ADDRSTRLEN, "%s", str); + *changbitmap |= BITMAP_SAVE_FILE; + *changbitmap |= BITMAP_IP_CHANGE; + *boottype = BOOT_LOCAL_IP_CHANGE; + } + } + + str = cJSON_GetStringValue(cJSON_GetObjectItem(param, "ServerIP1")); + if (str) + { + inet_aton(str, &addr.sin_addr); + if (addr.sin_addr.s_addr != csg.server_ip) + { + csg.server_ip = addr.sin_addr.s_addr; + *changbitmap |= BITMAP_SAVE_FILE; + *boottype = BOOT_LOCAL_SERVER_IP_CHANGE; + } + } + + int port_tmp = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "ServerIP1_Port")); + if (port_tmp > 0 && port_tmp != csg.server_port) + { + csg.server_port = port_tmp; + *changbitmap |= BITMAP_SAVE_FILE; + *boottype = BOOT_LOCAL_SERVER_PORT_CHANGE; + } + + str = cJSON_GetStringValue(cJSON_GetObjectItem(param, "ChannelType")); + if (str) + { + for(i = 0; i < PD_PORT_SUM; i++) + { + if ('\0' == *str) + { + break; + } + + type = (*str - '0') << 4; + str++; + + type += *str - '0'; + str++; + + if (dau_vport_to_port(i + 1, &unit, &port) != E_NONE) + { + break; + } + + dau_port_type_set(unit, port, type); + + dau_reg_port_write(unit, port); + + *changbitmap |= BITMAP_SAVE_FILE; // save file + } + } + + return; +} + +void _debug_parse_user_param(cJSON *root, unsigned int *changbitmap) +{ + unsigned int temp; + cJSON *param = cJSON_GetObjectItem(root,"EquipmentUserParam"); + + if (!param) + { + return; + } + + pd_config.config.power_frequency = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "SystemFrequency")); + temp = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "SyncMode")); + if (pd_config.config.sync_mode != temp) + { + pd_config.config.sync_mode = (uint8_t)temp; + *changbitmap |= BITMAP_SAVE_FILE; + pd_sync_mode_set(); + } + pd_config.config.TimingSyncMode = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "TimingSyncMode")); + temp = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "DataAcquisitionCycle")); + if (pd_config.config.real_period != temp) + { + pd_config.config.real_period = (uint8_t)temp; + *changbitmap |= BITMAP_SAVE_FILE; + } + temp = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "TrendDataReportCycle")); + if (pd_config.config.trend_period != temp) + { + pd_config.config.trend_period = (uint8_t)temp; + *changbitmap |= BITMAP_SAVE_FILE; + } + temp = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "HeartbeatCycle")); + if (pd_config.config.heartbeat_period != temp) + { + pd_config.config.heartbeat_period = (uint8_t)temp; + *changbitmap |= BITMAP_SAVE_FILE; + } + pd_config.config.NumberWindowsOfPhase = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NumberWindowsOfPhase")); + pd_config.config.SyncTime = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "SyncTime")); + pd_config.config.AlarmPeriod = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "AlarmPeriod")); + pd_config.config.TimePrpsStrategy = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "TimePrpsStrategy")); + pd_config.config.storage_trend = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "StorageTrent")); + pd_config.config.StorageAlarm = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "StorageAlarm")); + pd_config.config.StorageRun = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "StorageRun")); + pd_config.config.storage_real = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "StorageFtPRPS")); + pd_config.config.limit_event_time = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "LimitEventStorageTime")); + pd_config.config.limit_event_cnt = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "LimitEventStorageCount")); + pd_config.config.limit_event_interval = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "LimitEventStorageSpace")); + pd_config.config.is_prps_save = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "prpsSaveEnable")); + + return; +} + +void _debug_parse_port_param(cJSON *root) +{ + pd_port_config_old_t *config = NULL; + cJSON *port_array = NULL; + cJSON *param = NULL; + int size = 0; + uint8_t vport = 0; + uint8_t unit = 0; + uint8_t port = 0; + uint8_t i = 0; + + port_array = cJSON_GetObjectItem(root, "SensorConfigParam"); + if (!port_array) + { + return; + } + + size = cJSON_GetArraySize(port_array); + for (i = 0; i < size; i++) + { + param = cJSON_GetArrayItem(port_array, i); + if (!param ) + { + continue; + } + + vport = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "ChannelNumber")); + if (dau_vport_to_port(vport, &unit, &port) != E_NONE) + { + continue; + } + config = &pd_config.port_config[unit][port].config; + + config->filter = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "FilterType")); + config->sensor_type = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "SensorType")); + config->phase_sequence = (uint8_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "PhaseSequence")); + config->NearSensor1_Number = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor1_Number")); + config->NearSensor1_Distance = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor1_Distance")); + config->Signal1_AttenuationRatio = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "Signal1_AttenuationRatio")); + config->NearSensor2_Number = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor2_Number")); + config->NearSensor2_Distance = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor2_Distance")); + config->Signal2_AttenuationRatio = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "Signal2_AttenuationRatio")); + config->NearSensor3_Number = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor3_Number")); + config->NearSensor3_Distance = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor3_Distance")); + config->Signal3_AttenuationRatio = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "Signal3_AttenuationRatio")); + config->NearSensor4_Number = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor4_Number")); + config->NearSensor4_Distance = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NearSensor4_Distance")); + config->Signal4_AttenuationRatio = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "Signal4_AttenuationRatio")); + config->event_threshold = (int16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "EventTriggerThreshold")); + config->event_counter = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "EventTriggerCounter")); + config->env_noise = (int16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "EnvironmentalNoiseThreshold")); + config->noise_reduction = (int16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "NoiseReductionLevel")); + config->UpperLimitOfDischargeAmplitude = (int16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "UpperLimitOfDischargeAmplitude")); + config->WaveTriggerThreshold = (int16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "WaveTriggerThreshold")); + config->PrescalerSampleValue = (uint16_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "PrescalerSampleValue")); + config->SamplePointsTotalTriggered = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "SamplePointsTotalTriggered")); + config->RecordStartAdr = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "RecordStartAdr")); + config->RecordStopAdr = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "RecordStopAdr")); + config->sample_rate = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "SampleRate")); + config->BeforeTriggerTime = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "BeforeTriggerTime")); + config->AfterTriggerTime = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "AfterTriggerTime")); + config->storage_event = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "StorageEvent")); + config->storage_wave = (uint32_t)cJSON_GetNumberValue(cJSON_GetObjectItem(param, "StorageAEWave")); + + pd_config.port_config[unit][port].filter_cfg = pd_config.port_config[unit][port].config.filter; + dau_port_config_set(unit, port); + dau_reg_port_write(unit, port); + } + + return; +} + +int32_t _debug_parse_json_to_cfg(cJSON *root) +{ + unsigned int changbitmap = 0; //bit0:need save + //bit1:local ip changed + BOOT_MSG boottype = BOOT_NONE; + + _debug_parse_fatcory_param(root, &changbitmap, &boottype); + _debug_parse_user_param(root, &changbitmap); + _debug_parse_port_param(root); + + if (BITMAP_SAVE_FILE == (changbitmap & BITMAP_SAVE_FILE)) + { + vtysh_config_save(); + if (BITMAP_IP_CHANGE == (changbitmap & BITMAP_IP_CHANGE)) + { + uint8_t mac[MAC_ADDR_LEN] = {0}; + mac_generate_from_ip(device_info.host_ip, mac); + memcpy(device_info.mac, mac, MAC_ADDR_LEN); + vtysh_device_save(); + vtysh_eth0_save(); + } + } + + if (boottype) + { + reboot_system(DBG_M_DEBUG, boottype); + } + + return E_NONE; +} + +/* 回复报文处理. */ +int32_t _debug_pkt_common_send(char *buf, uint16_t cmd, uint32_t data_len) +{ + debug_pkt_head_t *head = (debug_pkt_head_t*)buf; + uint16_t *tail = (uint16_t*)(buf + sizeof(debug_pkt_head_t) + data_len); + + _debug_pkt_head_int(head, cmd, data_len); + *tail = DEBUG_TAIL; + + if (write(debug_ctrl.fd_client, buf, sizeof(debug_pkt_head_t) + data_len + 2) <= 0) + { + DBG(DBG_M_DEBUG, "Write cmd %x ERROR\r\n", head->cmd); + return E_SYS_CALL; + } + + return E_NONE; +} + +/* 获取配置报文处理. */ +int32_t _debug_pkt_cfg_get(char *buf, int len) +{ + cJSON *root = NULL; + char *cfg = NULL; + char *cfg_json = NULL; + uint16_t len_pkt = 0; + + /* 获取 json 格式的配置文件. */ + root = _debug_save_cfg_to_json(); + if (!root) + { + return E_NOT_FOUND; + } + + /* 将 json 转为字符串. */ + cfg_json = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + len_pkt = strlen(cfg_json); + + /* 复制数据. */ + cfg = buf + sizeof(debug_pkt_head_t); + memcpy(cfg, cfg_json, len_pkt); + free(cfg_json); + + /* 发送数据. */ + return _debug_pkt_common_send(buf, DEBUG_CONFIG_GET, len_pkt); +} + +/* 设置报文处理. */ +int32_t _debug_pkt_cfg_set(char *buf, int len) +{ + char *cfg = buf + sizeof(debug_pkt_head_t); + cJSON *root = cJSON_Parse(cfg); + + if (!root) + { + DBG(DBG_M_DEBUG, "Parse json error!\r\n"); + return E_SYS_CALL; + } + + /* 将 json 配置转换成设备配置. */ + if (_debug_parse_json_to_cfg(root) != E_NONE) + { + cJSON_Delete(root); + return E_BAD_PARAM; + } + cJSON_Delete(root); + + /* 发送数据. */ + return _debug_pkt_common_send(buf, DEBUG_CONFIG_SET, 0); +} + +/* 重启报文处理. */ +int32_t _debug_pkt_reboot(char *buf, int len) +{ + /* 发送数据. */ + _debug_pkt_common_send(buf, DEBUG_REBOOT, 0); + + /* 重启. */ + reboot_system(DBG_M_DEBUG, BOOT_LOCAL_RESET); + + return E_NONE; +} + +/* 时间设置报文处理. */ +int32_t _debug_pkt_time_set(char *buf, int len) +{ + uint32_t *timestamp = (uint32_t*)(buf + sizeof(debug_pkt_head_t)); + + /* 配置时间. */ + time_set(*timestamp); + + /* 发送数据. */ + _debug_pkt_common_send(buf, DEBUG_TIME_SET, 0); + + return E_NONE; +} + +/* 数据手动设置报文处理. */ +int32_t _debug_pkt_manual_col(char *buf, int len) +{ + uint32_t *flag = (uint32_t*)(buf + sizeof(debug_pkt_head_t)); + + /* 修改标志位. */ + debug_ctrl.is_manual_col = (*flag) ? TRUE : FALSE; + + /* 发送数据. */ + return _debug_pkt_common_send(buf, DEBUG_NOISE_CAREFOR, 0); +} + +/* 获取校准系数报文处理. */ +int32_t _debug_pkt_adj_get(char *buf, int len) +{ + dau_port_reg_t *reg = NULL; + debug_pkt_adj_t *data = (debug_pkt_adj_t *)(buf + sizeof(debug_pkt_head_t)); + uint8_t unit = 0; + uint8_t port = 0; + uint8_t i = 0; + uint8_t j = 0; + + memset(data, 0, sizeof(debug_pkt_adj_t)); + for(i = 0; i < PD_PORT_SUM; i++) + { + if (dau_vport_to_port(i + 1, &unit, &port) != E_NONE) + { + break; + } + + reg = &dau[unit]->port_reg[port]; + + for(j = 0; j < 4; j++) + { + data->vport[i].point[j] = reg->ASPR[j]; + } + for(j = 0; j < 5; j++) + { + data->vport[i].param_a[j] = reg->AFAR[j]; + } + for(j = 0; j < 5; j++) + { + data->vport[i].param_b[j] = reg->AFBR[j]; + } + } + + /* 发送数据. */ + return _debug_pkt_common_send(buf, DEBUG_ADJSUT_COEFFICIENT_GET, sizeof(debug_pkt_adj_t)); +} + +/* 获取校准系数报文处理. */ +int32_t _debug_pkt_adj_set(char *buf, int len) +{ + dau_port_reg_t *reg = NULL; + debug_pkt_adj_t *data = (debug_pkt_adj_t*)(buf + sizeof(debug_pkt_head_t)); + int8_t *ret = (int8_t*)(buf + sizeof(debug_pkt_head_t)); + uint8_t unit = 0; + uint8_t port = 0; + uint8_t i = 0; + uint8_t j = 0; + + for(i = 0; i < PD_PORT_SUM; i++) + { + if (dau_vport_to_port(i + 1, &unit, &port) != E_NONE) + { + break; + } + + reg = &dau[unit]->port_reg[port]; + + for(j = 0; j < 4; j++) + { + reg->ASPR[j] = data->vport[i].point[j]; + } + reg->ASPR[j] = 65535; + for(j = 0; j < 5; j++) + { + reg->AFAR[j] = data->vport[i].param_a[j]; + } + for(j = 0; j < 5; j++) + { + reg->AFBR[j] = data->vport[i].param_b[j]; + } + dau_reg_port_write(unit, port); + } + + /* 发送数据. */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + dau_param_save(unit); + } + + *ret = 1; + return _debug_pkt_common_send(buf, DEBUG_ADJSUT_COEFFICIENT_SET, 1); +} + + +/* 获取运行状态报文处理. */ +int32_t _debug_pkt_status_get(char *buf, int len) +{ + debug_pkt_status_t *status = (debug_pkt_status_t*)(buf + sizeof(debug_pkt_head_t)); + + status->idx = 0; + status->UTC_TimeScale = time(NULL); + status->F50Hz_Frequency = dau_power_frequency_get(); + status->F50Hz_SynStatus = pd_state.sync; + status->dau_status = dau_connect_get(); + status->sensor_status = 0; + status->is_server_link = csg.is_connect; + status->version = version_hex; + status->communication_time = csg.communication_time; + status->run_time = status->UTC_TimeScale - start_time; + + /* 发送数据. */ + return _debug_pkt_common_send(buf, DEBUG_RUN_STATUS_GET, sizeof(debug_pkt_status_t)); +} + +/* CMU 升级报文处理. */ +int32_t _debug_pkt_mcu_upgrade(char *buf, int len) +{ + debug_pkt_head_t *head = (debug_pkt_head_t*)buf; + char *date = buf + sizeof(debug_pkt_head_t); + int fd = 0; + + /* 保存文件. */ + fd = open(DEBUG_CMU_FILE_BAK, O_WRONLY | O_CREAT | O_TRUNC, 0777); + if (fd <= 0) + { + DBG(DBG_M_DEBUG, "Open " DEBUG_CMU_FILE_BAK " ERROR\r\n"); + return E_SYS_CALL; + } + + if (write(fd, date, head->len) != head->len) + { + DBG(DBG_M_DEBUG, "Write " DEBUG_CMU_FILE_BAK " ERROR\r\n"); + return E_SYS_CALL; + } + close(fd); + + if (rename(DEBUG_CMU_FILE_BAK, DEBUG_CMU_FILE) < 0) + { + DBG(DBG_M_DEBUG, "Rename " DEBUG_CMU_FILE_BAK " ERROR\r\n"); + return E_SYS_CALL; + } + + /* 发送回复. */ + _debug_pkt_common_send(buf, DEBUG_ARM_UPGRADE, 0); + + reboot_system(DBG_M_DEBUG, BOOT_LOCAL_ARM_UPGRADE); + + return E_NONE; +} + +/* DAU 升级报文处理. */ +int32_t _debug_pkt_dau_upgrade(char *buf, int len, uint8_t bitmap) +{ + debug_pkt_head_t *head = (debug_pkt_head_t*)buf; + char *date = buf + sizeof(debug_pkt_head_t); + int8_t *ret = (int8_t*)(buf + sizeof(debug_pkt_head_t)); + int fd = 0; + + fd = open(DEBUG_DAU_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd <= 0) + { + DBG(DBG_M_DEBUG, "Open " DEBUG_DAU_FILE " ERROR\r\n"); + return E_SYS_CALL; + } + + if (write(fd, date, head->len) != head->len) + { + DBG(DBG_M_DEBUG, "Write " DEBUG_DAU_FILE " ERROR\r\n"); + return E_SYS_CALL; + } + close(fd); + + /* 发送回复. */ + if (dau_update() != E_NONE) + { + *ret = 0; + } + else + { + *ret = 1; + } + + _debug_pkt_common_send(buf, head->cmd, 1); + + reboot_system(DBG_M_DEBUG, BOOT_LOCAL_FPGA_UPGRADE); + + return E_NONE; +} + +int32_t _debug_all_upgrade(char *buf, int len) +{ + debug_pkt_head_t *head = (debug_pkt_head_t*)buf; + char *date = buf + sizeof(debug_pkt_head_t); + //int8_t *ret = (int8_t*)(buf + sizeof(debug_pkt_head_t)); + int fd = 0; + char upgrade_file[128] = {0}; + if (buf == NULL) + { + return E_BAD_PARAM; + } + + printh("cmd:0x%x len:%d\n", head->cmd, head->len); + snprintf(upgrade_file, 127, "/home/gis/%s", UPGRADE_SOFTWARE); + fd = open(upgrade_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd <= 0) + { + DBG(DBG_M_DEBUG, "Open " DEBUG_DAU_FILE " ERROR\r\n"); + return E_SYS_CALL; + } + + if (write(fd, date, head->len) != head->len) + { + DBG(DBG_M_DEBUG, "Write " DEBUG_DAU_FILE " ERROR\r\n"); + return E_SYS_CALL; + } + close(fd); + return _debug_pkt_common_send(buf, head->cmd, 1); +} + +/* 报文校验. */ +int32_t _debug_pkt_check(char *buf, int len) +{ + debug_pkt_head_t *head = (debug_pkt_head_t*)buf; + uint16_t *tail = (uint16_t*)(buf + len - 2); + + if (*tail != DEBUG_TAIL) + { + return E_ERROR; + } + + if(head->head != DEBUG_HEAD) + { + return E_ERROR; + } + + return E_NONE; +} + +/* 调试工具报文数据处理. */ +int32_t _debug_pkt_process(char *buf, int32_t len) +{ + debug_pkt_head_t *head = (debug_pkt_head_t*)buf; + + /* 报文格式检查. */ + LD_E_RETURN(DBG_M_DEBUG, _debug_pkt_check(buf, len)); + + /* 报文处理. */ + switch(head->cmd) + { + case DEBUG_CONFIG_GET: + _debug_pkt_cfg_get(buf, len); + break; + case DEBUG_CONFIG_SET: + _debug_pkt_cfg_set(buf, len); + break; + case DEBUG_ALARM_CONFIG_GET: + break; + case DEBUG_REBOOT: + _debug_pkt_reboot(buf, len); + break; + case DEBUG_TIME_SET: + _debug_pkt_time_set(buf, len); + break; + case DEBUG_NOISE_CAREFOR: + _debug_pkt_manual_col(buf, len); + break; + case DEBUG_RUN_STATUS_GET: + _debug_pkt_status_get(buf, len); + break; + case DEBUG_ADJSUT_COEFFICIENT_GET: + _debug_pkt_adj_get(buf, len); + break; + case DEBUG_ADJSUT_COEFFICIENT_SET: + _debug_pkt_adj_set(buf, len); + break; + case DEBUG_ARM_UPGRADE: + _debug_pkt_mcu_upgrade(buf, len); + break; + case DEBUG_FPGA1_UPGRADE: + _debug_pkt_dau_upgrade(buf, len, 0x01); + break; + case DEBUG_FPGA2_UPGRADE: + _debug_pkt_dau_upgrade(buf, len, 0x02); + break; + case DEBUG_FPGA3_UPGRADE: + _debug_pkt_dau_upgrade(buf, len, 0x04); + break; + case DEBUG_FPGA4_UPGRADE: + _debug_pkt_dau_upgrade(buf, len, 0x08); + break; + case DEBUG_FPGA_UPGRADE: + _debug_pkt_dau_upgrade(buf, len, 0x0f); + break; + case DEBUG_UPGRADE_ALL: + _debug_all_upgrade(buf, len); + //init_upgrade_manage(); + break; + + default: + DBG(DBG_M_DEBUG, "Debug not support cmd:%x\n", head->cmd); + break; + } + + return E_NONE; +} + +/* tcp 连接粘包处理函数. */ +int32_t _debug_pkt_recv_adhesion(char *buf, int len, int *len_recv) +{ + debug_pkt_head_t *head = NULL; + static int len_pkt = 0; + static int bytes_cnt = 0; + static int state = 0; + + if (0 == state) + { + /* 首包处理. */ + head = (debug_pkt_head_t*)buf; + bytes_cnt = 0; + + if (head->len > 10485000) + { + /* 报文太长不处理. */ + return -1; + } + else if (0x55aa == head->head) + { + /* 报文头处理, 置标志位. */ + state = 1; + + /* 计算剩余报文长度, 复制数据, 并计算收包总长度. */ + len_pkt = head->len + 10 - len; + memcpy(buf_cmd + bytes_cnt, buf, len); + bytes_cnt += len; + + /* 计算下一包应该接收多大的数据. */ + if (len_pkt > DEBUG_BUG_SIZE) + { + *len_recv = DEBUG_BUG_SIZE; + } + else + { + *len_recv = len_pkt; + } + } + else + { + /* 在状体 0 下, 收到的报文不是以 0x55aa 开头, 无效报文不处理. */ + return -1; + } + } + else if(1 == state) + { + /* 报文内容处理. */ + /* 计算剩余报文长度, 复制数据, 并计算收包总长度. */ + len_pkt -= len; + memcpy(buf_cmd + bytes_cnt, buf, len); + bytes_cnt += len; + + /* 计算下一包应该接收多大的数据. */ + if (len_pkt > DEBUG_BUG_SIZE) + { + *len_recv = DEBUG_BUG_SIZE; + } + else + { + *len_recv = len_pkt; + } + } + + /* 报文没有收全, 继续等待数据. */ + if (len_pkt > 0) + { + return 0; + } + + state = 0; + return bytes_cnt; +} + +/* 调试工具报文接收处理线程. */ +void *_debug_pkt_recv_handle(void *arg) +{ + struct sockaddr_in client = {0}; + char *buf = debug_ctrl.buf; + socklen_t sockt_len = 0; + int len = 0; + int len_pkt = 0; + int len_recv = DEBUG_BUG_SIZE; + + while (!is_system_init) + { + sleep(1); + } + + /* 监听端口. */ + if ((listen(debug_ctrl.fd, 5)) != 0) + { + log_err(LOG_DEBUG, "ERROR at socket listen return %s!", safe_strerror(errno)); + return NULL; + } + + while(1) + { + /* 连接调试工具. */ + sockt_len = sizeof(client); + debug_ctrl.fd_client = accept(debug_ctrl.fd, (struct sockaddr*)&client, &sockt_len); + if (debug_ctrl.fd_client < 0) + { + log_err(LOG_DEBUG, "ERROR at socket accept return %s!", safe_strerror(errno)); + continue; + } + /* 连接成功. */ + while(1) + { + // 开启保活,保活参数 表示60秒内无交互后,每隔6秒检测一次,40次都没得到响应时会断开连接。 + int keep_alive = 1; + int keep_idle = 60; + int keep_interval = 6; + int keep_count = 40; + if (setsockopt(debug_ctrl.fd_client, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_alive))) + { + log_err(LOG_DEBUG, "Error setsockopt(SO_KEEPALIVE) failed, return %s!", safe_strerror(errno)); + return NULL; + } + if (setsockopt(debug_ctrl.fd_client, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle))) + { + log_err(LOG_DEBUG, "Error setsockopt(TCP_KEEPIDLE) failed, return %s!", safe_strerror(errno)); + return NULL; + } + if (setsockopt(debug_ctrl.fd_client, SOL_TCP, TCP_KEEPINTVL, (void *)&keep_interval, sizeof(keep_interval))) + { + log_err(LOG_DEBUG, "Error setsockopt(TCP_KEEPINTVL) failed, return %s!", safe_strerror(errno)); + return NULL; + } + if (setsockopt(debug_ctrl.fd_client, SOL_TCP, TCP_KEEPCNT, (void *)&keep_count, sizeof(keep_count))) + { + log_err(LOG_DEBUG, "Error setsockopt(TCP_KEEPCNT) failed, return %s!", safe_strerror(errno)); + return NULL; + } + + /* 读取数据. */ + len = read(debug_ctrl.fd_client, buf, len_recv); + if (len <= 0) + { + /* 连接中断, 关闭 socket 和手动采样. */ + DBG(DBG_M_DEBUG, "Read len %d, close fd!\r\n", len); + debug_ctrl.is_manual_col = FALSE; + close(debug_ctrl.fd_client); + break; + } + + /* 粘包处理. */ + len_pkt = _debug_pkt_recv_adhesion(buf, len, &len_recv); + if (len_pkt <= 0) + { + continue; + } + + _debug_pkt_process(buf_cmd, len_pkt); + len_recv = DEBUG_BUG_SIZE; + } + } + + return NULL; +} + +/* 用于定时将端口的状态发送给调试软件. */ +void* _debug_port_state_get(void *arg) +{ + /* 如果手动采样开启, 则发送数据. */ + if (debug_ctrl.is_manual_col) + { + dau_port_state_get(NULL); + } + + /* 重新加入定时器. */ + mtimer_add(_debug_port_state_get, NULL, 1, "DEBUG_PORT_STATE_GET"); + return NULL; +} + +/* Interface functions -------------------------------------------------------*/ +/* 调试工具初始化. */ +int32_t debug_handle_init(void) +{ + struct sockaddr_in server; + struct sched_param param; + pthread_attr_t attr; + pthread_t pid; + int32_t rv = 0; + int fd = 0; + int opt = 1; + + /* 创建 socket. */ + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + { + log_err(LOG_DEBUG, "ERROR at socket create return %s!", safe_strerror(errno)); + return E_SYS_CALL; + } + + + /* fd 为需要端口复用的套接字. */ + opt = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt)); + + /* 绑定端口. */ + bzero(&server, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = htonl(INADDR_ANY); + server.sin_port = htons(DEBUG_MANAGE_TOOL_PORT); + if(bind(fd, (struct sockaddr*)&server, sizeof(server)) < 0) + { + log_err(LOG_DEBUG, "ERROR at socket bind return %s!", safe_strerror(errno)); + close(fd); + return E_SYS_CALL; + } + + /* 保存数据. */ + debug_ctrl.fd = fd; + + /* 初始化报文处理线程. */ + /* 配置线程RR调度, 优先级25. */ + pthread_attr_init(&attr); + param.sched_priority = 25; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _debug_pkt_recv_handle, NULL); + if (rv != 0) + { + log_err(LOG_DEBUG, "PD can't create debug pthread %d!", rv); + return E_SYS_CALL; + } + else + { + thread_m_add("PD_DEBUG", pid); + } + pthread_attr_destroy(&attr); + + /* 每秒上传 DAU 状态寄存器. */ + mtimer_add(_debug_port_state_get, NULL, 1, "DEBUG_PORT_STATE_GET"); + + return E_NONE; +} + +/* 数据手动上传报文处理. */ +int32_t debug_pkt_port_state_post(void) +{ + char *buf = debug_ctrl.buf_post; + debug_pkt_port_t *data = (debug_pkt_port_t *)(buf + sizeof(debug_pkt_head_t)); + uint8_t i = 0; + uint8_t unit = 0; + uint8_t port = 0; + + if (!debug_ctrl.is_manual_col) + { + return E_NONE; + } + + for(i = 0; i < PD_PORT_SUM; i++) + { + if (E_NONE == dau_vport_to_port(i + 1, &unit, &port)) + { + data->vport[i].value_adc = dau[unit]->port_state_reg.MSRR[port]; + data->vport[i].value_adj = dau[unit]->port_state_reg.AVR[port]; + } + else + { + data->vport[i].value_adc = 0; + data->vport[i].value_adj = -800; + } + } + + /* 发送数据. */ + return _debug_pkt_common_send(buf, DEBUG_NOISE_POST, sizeof(debug_pkt_port_t)); +} + +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/a_process/pd_gis.c b/app/lib/a_process/pd_gis.c new file mode 100755 index 0000000..eb29c7d --- /dev/null +++ b/app/lib/a_process/pd_gis.c @@ -0,0 +1,795 @@ +/****************************************************************************** + * file lib/process/gis.c + * author YuLiang + * version 1.0.0 + * date 23-Feb-2023 + * brief This file provides all the gis operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2023 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef CFG_DEV_TYPE_LAND_PD +/* 标准C库头文件. */ + +/* 用户代码头文件. */ +#include "pd_dau.h" +#include "pd_main.h" + +/* Private define ------------------------------------------------------------*/ +#define GIS_MIN_VALUE PD_GIS_MIN_VALUE // 特高频局放的采集数值下限 -800 (-80.0dBm). + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ + +/* Private function prototypes -----------------------------------------------*/ +extern void _gis_prps_default(int16_t *prps); +extern void _gis_prps_denoise(uint8_t unit, uint8_t port); +extern void _gis_prps_denoise_relevance(uint8_t unit, uint8_t port); +extern void _gis_prps_denoise_statistics(uint8_t unit, uint8_t port); +extern void _gis_denoise(uint8_t unit, uint8_t port); +extern void _gis_denoise_relevance(uint8_t unit, uint8_t port); +extern void _gis_denoise_statistics(uint8_t unit, uint8_t port); +extern void _gis_event(uint8_t unit, uint8_t port); +extern void _gis_trend(uint8_t unit, uint8_t port, uint16_t trend_sec); + +dau_port_func_t gis_func = +{ + _gis_prps_default, + _gis_prps_denoise, + _gis_prps_denoise_relevance, + _gis_prps_denoise_statistics, + _gis_denoise, + _gis_denoise_relevance, + _gis_denoise_statistics, + _gis_event, + _gis_trend +}; + +/* Internal functions --------------------------------------------------------*/ +void _gis_prps_default(int16_t *prps) +{ + uint16_t i = 0; + + for(i = 0; i < PD_PRPS_NUM; i++) + { + prps[i] = GIS_MIN_VALUE; + } +} + +/* 通道的实时数据 (自动 / 手动) 降噪的计算处理 (用于实时PRPS的临时显示使用). */ +void _gis_prps_denoise(uint8_t unit, uint8_t port) +{ + pd_prps_t *data = pd_data.denoise; + int16_t *buf = NULL; + int16_t *buf_prps = NULL; + uint8_t pf_cnt = 0; + uint8_t i = 0; + uint16_t manual_noise_reduction = 0; + int16_t manual_noise_level = 0; + int16_t noise_level = 0; + int32_t phase_avg = 0; + int32_t temp = 0; + + /* 噪声传感器在关联降噪中运算. */ + if (PD_SEN_TYPE_NOISE == pd_config.port_config[unit][port].config.sensor_type + || !pd_config.port_config[unit][port].is_concern + || !dau[unit]->port_state[port].is_complete) + { + return; + } + + /* 非噪声通道处理. */ + for(pf_cnt = 0; pf_cnt < PD_POWER_FRE; pf_cnt++) + { + /* 左移 7 位正好是乘以 128(PD_PHASE_NUM). */ + buf = &data->data[unit][port].data[pf_cnt << 7]; + buf_prps = &dau[unit]->port_state[port].prps[pf_cnt << 7]; + + phase_avg = 0; + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf_prps[i] < GIS_MIN_VALUE) + { + buf[i] = GIS_MIN_VALUE; + } + else + { + buf[i] = buf_prps[i]; + } + + phase_avg += buf[i]; + } + + phase_avg = (phase_avg / PD_PHASE_NUM); + noise_level = phase_avg + pd_config.port_config[unit][port].config.noise_reduction; // 普通降噪水平 默认:30 单位:0.1dBm + dau[unit]->port_state[port].noise_level[pf_cnt] = noise_level; // 局放计数时的比较幅值. + + /* 未配置通道类型的不再进行下去. */ + if (pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_SIG + && pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_SIG_NOISE) + { + continue; + } + + if (pd_config.port_config[unit][port].auto_noise_reduction) + { + /* 自适应降噪. */ + temp = phase_avg - GIS_MIN_VALUE; + + /* 低于平均值的不显示, 低于普通降噪水平的减去平均值, 余下的原值显示. */ + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf[i] <= phase_avg) + { + buf[i] = GIS_MIN_VALUE; + } + else if(buf[i] <= noise_level) + { + buf[i] -= temp; + } + } + + } + else if(pd_config.port_config[unit][port].manual_noise_reduction) + { + /* 手动降噪. */ + manual_noise_reduction = pd_config.port_config[unit][port].manual_noise_reduction; + if (manual_noise_reduction > 800) + { + manual_noise_reduction = 800; + manual_noise_level = 0; + } + else + { + manual_noise_level = GIS_MIN_VALUE + manual_noise_reduction; + } + + for(i = 0; i < PD_PHASE_NUM; i++) + { + /* 手动降噪水平小于普通降噪水平, 需要考虑降噪水平, 否则不考虑普通降噪水平. */ + if (manual_noise_level < noise_level) + { + /* 低于手动降噪水平的不显示, 低于普通降噪水平的减去手动降噪水平, 余下的原值显示. */ + if (buf[i] <= manual_noise_level) + { + buf[i] = GIS_MIN_VALUE; + } + else if( buf[i] <= noise_level) + { + buf[i] -= manual_noise_reduction; + } + } + else + { + /* 低于手动降噪水平的不显示, 余下的原值显示. */ + if (buf[i] <= manual_noise_level) + { + buf[i] = GIS_MIN_VALUE; + } + } + } + } + } +} + +/* 通道的实时数据关联降噪的计算处理 (用于实时PRPS的临时显示使用). */ +void _gis_prps_denoise_relevance(uint8_t unit, uint8_t port) +{ + pd_prps_t *data = pd_data.denoise; + int16_t *buf = NULL; + int16_t *buf_prps = NULL; + int16_t *buf_noise = NULL; + uint8_t unit_r = 0; // 关联降噪的单元. + uint8_t port_r = 0; // 关联降噪的端口. + uint8_t pf_cnt = 0; + uint8_t i = 0; + int16_t noise_level = 0; + int32_t phase_avg = 0; + + if (pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_NOISE + || !dau[unit]->port_state[port].is_complete) + { + return; + } + + /* 噪声通道处理. */ + for(pf_cnt = 0; pf_cnt < PD_POWER_FRE; pf_cnt++) + { + /* 左移 7 位正好是乘以 128(PD_PHASE_NUM). */ + buf = &data->data[unit][port].data[pf_cnt << 7]; + buf_prps = &dau[unit]->port_state[port].prps[pf_cnt << 7]; + + phase_avg = 0; + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf_prps[i] < GIS_MIN_VALUE) + { + buf[i] = GIS_MIN_VALUE; + } + else + { + buf[i] = buf_prps[i]; + } + + phase_avg += buf[i]; + } + + phase_avg = (phase_avg / PD_PHASE_NUM); + noise_level = phase_avg + pd_config.port_config[unit][port].config.noise_reduction; // 普通降噪水平 默认:30 单位:0.1dBm + dau[unit]->port_state[port].noise_level[pf_cnt] = noise_level; // 局放计数时的比较幅值. + + for(unit_r = 0; unit_r < PD_DAU_SUM; unit_r++) + { + if (!dau[unit_r]) + { + continue; + } + + for(port_r = 0; port_r < dau[unit_r]->port_num; port_r++) + { + if (!pd_config.port_config[unit_r][port_r].r_noise_reduction + || !dau[unit_r]->port_state[port_r].is_complete + || (pd_config.port_config[unit_r][port_r].config.sensor_type != PD_SEN_TYPE_SIG + && pd_config.port_config[unit_r][port_r].config.sensor_type != PD_SEN_TYPE_SIG_NOISE)) + { + continue; + } + + buf_noise = &data->data[unit][port].data[pf_cnt << 7]; + buf = &data->data[unit_r][port_r].data[pf_cnt << 7]; + + /* 噪声通道工频内的第一值比噪声通道的平均降噪等级还要大. */ + if (buf_noise[0] > noise_level) + { + if (pf_cnt > 0) + { + data->data[unit_r][port_r].data[(pf_cnt << 7) - 1] = GIS_MIN_VALUE; + } + + buf[0] = GIS_MIN_VALUE; + buf[1] = GIS_MIN_VALUE; + } + + if (buf_noise[PD_PHASE_NUM - 1] > noise_level) + { + if (pf_cnt < 49) + { + buf[PD_PHASE_NUM] = GIS_MIN_VALUE; + } + + buf[PD_PHASE_NUM - 1] = GIS_MIN_VALUE; + buf[PD_PHASE_NUM - 2] = GIS_MIN_VALUE; + } + + for(i = 1; i < PD_PHASE_NUM - 1; i++) + { + if (buf_noise[i] > noise_level) + { + buf[i-1] = GIS_MIN_VALUE; + buf[i ] = GIS_MIN_VALUE; + buf[i+1] = GIS_MIN_VALUE; + } + } + } + } + } +} + +/* 通道的实时数据统计信息 (用于实时 PRPS 的临时显示使用). */ +void _gis_prps_denoise_statistics(uint8_t unit, uint8_t port) +{ + pd_prps_t *data = pd_data.denoise; + int16_t *buf = NULL; + uint8_t pf_cnt = 0; + uint8_t i = 0; + int16_t noise_level = 0; + uint16_t cnt = 0; + int16_t max = GIS_MIN_VALUE; + int32_t avg = 0; + + if (!pd_config.port_config[unit][port].is_concern) + { + return; + } + + data->data[unit][port].is_valid = dau[unit]->port_state[port].is_complete; + if (!dau[unit]->port_state[port].is_complete) + { + return; + } + + /* 数值的计算 最大值, 平均值, 放电次数. */ + for(pf_cnt = 0; pf_cnt < PD_POWER_FRE; pf_cnt++) + { + buf = &data->data[unit][port].data[pf_cnt << 7]; + noise_level = dau[unit]->port_state[port].noise_level[pf_cnt]; + + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf[i] > max ) + { + max = buf[i]; + } + + if (buf[i] > noise_level) + { + avg += buf[i]; + cnt++; + } + } + } + + data->data[unit][port].max = max; + data->data[unit][port].cnt = cnt; + data->data[unit][port].avg = cnt ? avg / cnt : GIS_MIN_VALUE; +} + +/* 通道的实时数据 (自动 / 手动) 降噪的计算处理. */ +void _gis_denoise(uint8_t unit, uint8_t port) +{ + pd_prps_t *data = &pd_data.real; + int16_t *buf = NULL; + int16_t *buf_real = NULL; + uint8_t pf_cnt = 0; + uint8_t i = 0; + uint16_t manual_noise_reduction = 0; + int16_t manual_noise_level = 0; + int16_t noise_level = 0; + int32_t avg = 0; + int32_t phase_avg = 0; + int32_t temp = 0; + + /* 噪声传感器在关联降噪中运算. */ + if (PD_SEN_TYPE_NOISE == pd_config.port_config[unit][port].config.sensor_type) + { + return; + } + + for(pf_cnt = 0; pf_cnt < PD_POWER_FRE; pf_cnt++) + { + /* 计算平均值. */ + buf = &dau[unit]->port_state[port].prps[pf_cnt << 7]; + buf_real = &data->data[unit][port].data[pf_cnt << 7]; + phase_avg = 0; + + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf[i] < GIS_MIN_VALUE) + { + buf_real[i] = GIS_MIN_VALUE; + } + else + { + buf_real[i] = buf[i]; + } + + phase_avg += buf_real[i]; + } + avg += phase_avg; + + phase_avg = (phase_avg / PD_PHASE_NUM); + noise_level = phase_avg + pd_config.port_config[unit][port].config.noise_reduction; // 普通降噪水平 默认:30 单位:0.1dBm + + /* 未配置通道类型的不再进行下去. */ + if (pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_SIG + && pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_SIG_NOISE) + { + continue; + } + + /* 降噪处理. */ + if (100 == pd_config.port_config[unit][port].config.env_noise) // 自动降噪. + { + temp = phase_avg - GIS_MIN_VALUE; + + /* 低于平均值的不显示, 低于普通降噪水平的减去平均值, 余下的原值显示. */ + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf_real[i] <= phase_avg) + { + buf_real[i] = GIS_MIN_VALUE; + } + else if(buf_real[i] <= noise_level) + { + buf_real[i] -= temp; + } + } + } + else if(0 == pd_config.port_config[unit][port].config.env_noise) // 不降噪. + { + NULL; + } + else // 人工降噪. + { + manual_noise_reduction = pd_config.port_config[unit][port].config.env_noise; + if (manual_noise_reduction > 800) + { + manual_noise_reduction = 800; + manual_noise_level = 0; + } + else + { + manual_noise_level = GIS_MIN_VALUE + manual_noise_reduction; + } + + for(i = 0; i < PD_PHASE_NUM; i++) + { + /* 手动降噪水平小于普通降噪水平, 需要考虑降噪水平, 否则不考虑普通降噪水平. */ + if (manual_noise_level < noise_level) + { + /* 低于手动降噪水平的不显示, 低于普通降噪水平的减去手动降噪水平, 余下的原值显示. */ + if (buf_real[i] <= manual_noise_level) + { + buf_real[i] = GIS_MIN_VALUE; + } + else if( buf_real[i] <= noise_level) + { + buf_real[i] -= manual_noise_reduction; + } + } + else + { + /* 低于手动降噪水平的不显示, 余下的原值显示. */ + if (buf_real[i] <= manual_noise_level) + { + buf_real[i] = GIS_MIN_VALUE; + } + } + } + } + } + + /* 数据统计. */ + data->data[unit][port].avg_o = avg / PD_PRPS_NUM; +} + +/* 通道的实时数据关联降噪的计算处理. */ +void _gis_denoise_relevance(uint8_t unit, uint8_t port) +{ + pd_prps_t *data = &pd_data.real; + int16_t *buf = NULL; + int16_t *buf_real = NULL; + int16_t *buf_noise = NULL; + uint8_t unit_r = 0; // 关联降噪的单元. + uint8_t port_r = 0; // 关联降噪的端口. + uint8_t pf_cnt = 0; + uint8_t i = 0; + int16_t noise_level = 0; + int32_t avg = 0; + int32_t phase_avg = 0; + + if (pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_NOISE) + { + return; + } + + /* 噪声通道处理. */ + for(pf_cnt = 0; pf_cnt < PD_POWER_FRE; pf_cnt++) + { + /* 左移 7 位正好是乘以 128(PD_PHASE_NUM). */ + buf = &dau[unit]->port_state[port].prps[pf_cnt << 7]; + buf_real = &data->data[unit][port].data[pf_cnt << 7]; + + + phase_avg = 0; + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf[i] < GIS_MIN_VALUE) + { + buf_real[i] = GIS_MIN_VALUE; + } + else + { + buf_real[i] = buf[i]; + } + + phase_avg += buf[i]; + } + avg += phase_avg; + + phase_avg = (phase_avg / PD_PHASE_NUM); + noise_level = phase_avg + pd_config.port_config[unit][port].config.noise_reduction; // 普通降噪水平 默认:30 单位:0.1dBm + + for(unit_r = 0; unit_r < PD_DAU_SUM; unit_r++) + { + if (!dau[unit_r]) + { + continue; + } + + for(port_r = 0; port_r < dau[unit_r]->port_num; port_r++) + { + if (pd_config.port_config[unit_r][port_r].config.sensor_type != PD_SEN_TYPE_SIG_NOISE) + { + continue; + } + + buf_noise = &data->data[unit][port].data[pf_cnt << 7]; + buf = &data->data[unit_r][port_r].data[pf_cnt << 7]; + + /* 噪声通道工频内的第一值比噪声通道的平均降噪等级还要大. */ + if (buf_noise[0] > noise_level) + { + if (pf_cnt > 0) + { + data->data[unit_r][port_r].data[(pf_cnt << 7) - 1] = GIS_MIN_VALUE; + } + + buf[0] = GIS_MIN_VALUE; + buf[1] = GIS_MIN_VALUE; + } + + if (buf_noise[PD_PHASE_NUM - 1] > noise_level) + { + if (pf_cnt < 49) + { + buf[PD_PHASE_NUM] = GIS_MIN_VALUE; + } + + buf[PD_PHASE_NUM - 1] = GIS_MIN_VALUE; + buf[PD_PHASE_NUM - 2] = GIS_MIN_VALUE; + } + + for(i = 1; i < PD_PHASE_NUM - 1; i++) + { + if (buf_noise[i] > noise_level) + { + buf[i-1] = GIS_MIN_VALUE; + buf[i ] = GIS_MIN_VALUE; + buf[i+1] = GIS_MIN_VALUE; + } + } + } + } + } + + /* 数据统计. */ + data->data[unit][port].avg_o = avg / PD_PRPS_NUM; +} + +/* 通道的实时数据统计信息. */ +void _gis_denoise_statistics(uint8_t unit, uint8_t port) +{ + pd_prps_data_t *real_data = &pd_data.real.data[unit][port]; + int16_t *buf = NULL; + int16_t noise_level = 0; + int32_t avg = 0; + uint8_t pf_cnt = 0; + uint8_t i = 0; + + /* 初始化原始值. */ + if (pd_config.port_config[unit][port].config.env_noise > 0) + { + noise_level = GIS_MIN_VALUE + pd_config.port_config[unit][port].config.noise_reduction; + } + else + { + noise_level = -790; + } + + real_data->max = GIS_MIN_VALUE; + real_data->cnt = 0; + + for(i = 0; i < PD_PHASE_NUM; i++) + { + real_data->phase_max[i] = GIS_MIN_VALUE; + real_data->phase_sum[i] = 0; + real_data->phase_cnt[i] = 0; + } + + /* 数值的计算 最大值, 平均值, 放电次数. */ + for(pf_cnt = 0; pf_cnt < PD_POWER_FRE; pf_cnt++) + { + buf = &real_data->data[pf_cnt << 7]; + + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf[i] > real_data->phase_max[i]) + { + real_data->phase_max[i] = buf[i]; + } + + if (buf[i] > noise_level) + { + real_data->phase_sum[i] += buf[i]; + real_data->phase_cnt[i]++; + } + } + } + + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (real_data->phase_max[i] > real_data->max) + { + real_data->max = real_data->phase_max[i]; + } + + if (real_data->phase_cnt[i]) + { + avg += real_data->phase_sum[i]; + real_data->cnt += real_data->phase_cnt[i]; + } + + real_data->phase_avg[i] = real_data->phase_cnt[i] ? real_data->phase_sum[i] / real_data->phase_cnt[i] : GIS_MIN_VALUE; + } + + real_data->avg = real_data->cnt ? avg / real_data->cnt : GIS_MIN_VALUE; +} + +/* 通道事件计算. */ +void _gis_event(uint8_t unit, uint8_t port) +{ + pd_prps_data_t *real_data = &pd_data.real.data[unit][port]; + pd_event_t *event = &pd_data.event[unit][port]; + int16_t *buf = NULL; + int16_t event_thr = 0; + uint8_t pf_cnt = 0; + uint8_t i = 0; + + /* 只处理信号传感器. */ + if (pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_SIG + && pd_config.port_config[unit][port].config.sensor_type != PD_SEN_TYPE_SIG_NOISE) + { + return; + } + + event_thr = pd_config.port_config[unit][port].config.event_threshold + real_data->avg_o; + event->max = GIS_MIN_VALUE; + event->avg = 0; + event->cnt = 0; + for(i = 0; i < PD_PHASE_NUM; i++) + { + event->phase_max[i] = GIS_MIN_VALUE; + event->phase_sum[i] = 0; + event->phase_cnt[i] = 0; + } + + /* 计算超过阈值的放电次数. */ + for(pf_cnt = 0; pf_cnt < PD_POWER_FRE; pf_cnt++) + { + buf = &real_data->data[pf_cnt << 7]; + + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (buf[i] > event->phase_max[i]) + { + event->phase_max[i] = buf[i]; + } + + if (buf[i] > event_thr) + { + event->phase_sum[i] += buf[i]; + event->phase_cnt[i]++; + } + } + } + + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (event->phase_max[i] > event->max) + { + event->max = event->phase_max[i]; + } + + event->avg += event->phase_sum[i]; + event->cnt += event->phase_cnt[i]; + + event->phase_avg[i] = event->phase_cnt[i] ? event->phase_sum[i] / event->phase_cnt[i] : GIS_MIN_VALUE; + } + + event->avg = event->cnt ? event->avg / event->cnt : GIS_MIN_VALUE; +} + +void _gis_trend(uint8_t unit, uint8_t port, uint16_t trend_sec) +{ + pd_trend_data_t *trend_data = &pd_data.trend.data[unit][port]; + pd_prps_data_t *real_data = &pd_data.real.data[unit][port]; + dau_t *dau_node = dau[unit]; + int64_t phase_cmp = 0x8000000000000000; + uint8_t phase_cmp_idx = 0; + uint8_t i = 0; + + /* 初始化数据. */ + if (1 == trend_sec) + { + pd_data.trend.index = dau_ctrl.trend_idx++; + pd_data.trend.utc = pd_data.real.utc; + trend_data->noise = 0; + trend_data->max = GIS_MIN_VALUE; + trend_data->avg = 0; + trend_data->cnt = 0; + + for(i = 0; i < PD_PHASE_NUM; i++) + { + trend_data->phase_sum[i] = 0; + trend_data->phase_max[i] = GIS_MIN_VALUE; + trend_data->phase_cnt[i] = 0; + } + } + + /* 填充数据. */ + for(i = 0; i < PD_PHASE_NUM; i++) + { + if (real_data->phase_cnt[i]) + { + trend_data->phase_sum[i] += real_data->phase_sum[i]; + trend_data->phase_cnt[i] += real_data->phase_cnt[i]; + trend_data->avg += real_data->phase_sum[i]; + trend_data->cnt += real_data->phase_cnt[i]; + } + + if (real_data->phase_max[i] > trend_data->phase_max[i]) + { + trend_data->phase_max[i] = real_data->phase_max[i]; + } + + if (real_data->phase_max[i] > trend_data->max) + { + trend_data->max = real_data->phase_max[i]; + } + } + + trend_data->noise += real_data->avg_o; + + /* 计算数据. */ + if (trend_sec >= pd_config.config.trend_period * 60) + { + for(i = 0; i < PD_PHASE_NUM; i++) + { + trend_data->phase_avg[i] = trend_data->phase_cnt[i] ? trend_data->phase_sum[i] / trend_data->phase_cnt[i] : GIS_MIN_VALUE; + + /* 查找最大放电周期. */ + if (trend_data->phase_cnt[i] && trend_data->phase_sum[i] > phase_cmp) + { + phase_cmp_idx = i; + phase_cmp = trend_data->phase_sum[i]; + } + } + + trend_data->phase = 360 * phase_cmp_idx / PD_PHASE_NUM ; // 放电相位 + trend_data->avg = trend_data->cnt ? trend_data->avg / trend_data->cnt : GIS_MIN_VALUE; + trend_data->cnt = trend_data->cnt / trend_sec; // 每秒钟的脉冲计数值 + trend_data->noise = trend_data->noise / (10 * trend_sec) ; // 趋势数据中的底噪值: 单位 dBm. + trend_data->event_cnt = dau_ctrl.event_index[unit][port] - dau_ctrl.trend_event_index[unit][port]; + + /* 更新索引. */ + dau_ctrl.trend_event_index[unit][port] = dau_ctrl.event_index[unit][port]; + } +} +/* Interface functions -------------------------------------------------------*/ +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/a_process/pd_main.c b/app/lib/a_process/pd_main.c new file mode 100755 index 0000000..f58bdb4 --- /dev/null +++ b/app/lib/a_process/pd_main.c @@ -0,0 +1,649 @@ +/***************************************************************************** + * file lib/process/pd_main.c + * author YuLiang + * version 1.0.0 + * date 07-Feb-2023 + * brief This file provides all the partial discharge related operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef CFG_DEV_TYPE_LAND_PD +/* 标准C库头文件. */ +#include +#include +#include +#include +#include +#include + +#include "pd_cpld.h" +#include "pd_dau.h" +#include "pd_csg.h" +#include "pd_main.h" +#include "pd_storage.h" +#include "pd_dbg.h" + +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +pd_data_t pd_data; +pd_config_t pd_config; +pd_state_t pd_state; +cmd_node_t pd_port_node = +{ + PORT_NODE, + CONFIG_NODE, + 1, + NULL, +}; + +/* DAU 端口类型分类. */ +static const char *pd_sen_type_str[PD_SEN_TYPE_COUNT] = +{ + "", + "sig", + "noise", + "sig-noise" +}; + +/* Private function prototypes -----------------------------------------------*/ +extern int32_t _pd_port_str_to_unit_port(const char *port_str, uint8_t *unit, uint8_t *port); + +/* Internal functions --------------------------------------------------------*/ +/* 进入 DAU 端口模式. */ +CMD(pd_port_terminal, + pd_port_terminal_cmd, + "interface port WORD", + "Interface\n" + "Port\n" + "Port id: Ex: 1/1\n") +{ + return CMD_SUCCESS; + uint8_t unit = 0; + uint8_t port = 0; + int32_t vport = 0; + + /* 取出端口号. */ + if (_pd_port_str_to_unit_port(argv[0], &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + /* 计算虚拟端口. */ + vport = dau_port_to_vport(unit, port); + if (vport <= 0) + { + return CMD_ERR_NO_MATCH; + } + + pd_port_node.param_num = vport; + snprintf(pd_port_node.prompt, PD_PORT_PROMPT_LEN, "%%s(interface port %d/%d)# ", unit + 1, port + 1); + + vty->node = PORT_NODE; + + return CMD_SUCCESS; +} + +/* 配置端口类型. */ +CMD(pd_sensor_type, + pd_sensor_type_cmd, + "sensor-type (sig|noise|sig-noise)", + "Sensor type\n" + "Signal\n" + "Noise\n" + "Signal associate noise\n") +{ + return CMD_SUCCESS; + uint8_t sen_type = 0; + uint8_t unit = 0; + uint8_t port = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + for(sen_type = PD_SEN_TYPE_SIG; sen_type < PD_SEN_TYPE_COUNT; sen_type++) + { + if (strncmp(argv[0], pd_sen_type_str[sen_type], strlen(pd_sen_type_str[sen_type]))) + { + continue; + } + + pd_config.port_config[unit][port].config.sensor_type = sen_type; + } + + return CMD_SUCCESS; +} + +/* 配置端口降噪等级. */ +CMD(pd_noise_reduction, + pd_noise_reduction_cmd, + "noise-reduction <0-800>", + "Noise reduction\n" + "Noise reduction unit: 0.1dBm\n") +{ + return CMD_SUCCESS; + uint8_t unit = 0; + uint8_t port = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + pd_config.port_config[unit][port].config.noise_reduction = strtol(argv[0], NULL, 10); + + return CMD_SUCCESS; +} + +/* 配置端口降噪等级. */ +CMD(pd_noise_env, + pd_noise_env_cmd, + "noise-env <0-100>", + "Noise environment\n" + "Noise environment unit: dBm\n") +{ + return CMD_SUCCESS; + uint8_t unit = 0; + uint8_t port = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + pd_config.port_config[unit][port].config.env_noise = strtol(argv[0], NULL, 10); + + return CMD_SUCCESS; +} + +/* 事件阈值. */ +CMD(pd_evnet_thr, + pd_evnet_thr_cmd, + "evnet-thr <0-800>", + "Event threshold\n" + "Threshold: 0.1dBm\n") +{ + return CMD_SUCCESS; + uint8_t unit = 0; + uint8_t port = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + pd_config.port_config[unit][port].config.event_threshold = strtol(argv[0], NULL, 10); + + return CMD_SUCCESS; +} + +/* 通道滤波类型. */ +CMD(pd_evnet_cnt, + pd_evnet_cnt_cmd, + "evnet-cnt <1-1000>", + "Event conut threshold\n" + "Conut threshold\n") +{ + return CMD_SUCCESS; + uint8_t unit = 0; + uint8_t port = 0; + + /* 取出端口号. */ + if (dau_vport_to_port(pd_port_node.param_num, &unit, &port) != E_NONE) + { + return CMD_ERR_NO_MATCH; + } + + pd_config.port_config[unit][port].config.event_counter = strtol(argv[0], NULL, 10);; + + return CMD_SUCCESS; +} + +/* 实时数据上传间隔. */ +CMD(pd_real_interval, + pd_real_interval_cmd, + "real-interval <1-60>", + "Real data interval\n" + "Interval time: min\n") +{ + return CMD_SUCCESS; + pd_config.config.real_period = strtol(argv[0], NULL, 10); + + return CMD_SUCCESS; +} + +/* 趋势数据上传间隔. */ +CMD(pd_trend_interval, + pd_trend_interval_cmd, + "trend-interval <1-60>", + "Trend data interval\n" + "Interval time: min\n") +{ + return CMD_SUCCESS; + pd_config.config.trend_period = strtol(argv[0], NULL, 10); + + return CMD_SUCCESS; +} + +/* 心跳上传间隔. */ +CMD(pd_heartbeat_interval, + pd_heartbeat_interval_cmd, + "heartbeat-interval <1-60>", + "heartbeat interval\n" + "Interval time: min\n") +{ + return CMD_SUCCESS; + pd_config.config.heartbeat_period = strtol(argv[0], NULL, 10); + + return CMD_SUCCESS; +} + +/* 同步模式. */ +CMD(pd_sync_mode, + pd_sync_mode_cmd, + "sync-mode (pt|power|outside)", + "Synchronize mode\n" + "PT\n" + "Power\n" + "Outside") +{ + return CMD_SUCCESS; + uint8_t mode = 0; + + if (0 == strncmp(argv[0], "pt", 2)) + { + mode = PD_SYNC_PT; + } + else if (0 == strncmp(argv[0], "power", 5)) + { + mode = PD_SYNC_POWER; + } + else if (0 == strncmp(argv[0], "outside", 5)) + { + mode = PD_SYNC_OUTSIDE; + } + else + { + mode = 0; + } + + pd_config.config.sync_mode = mode; + pd_sync_mode_set(); + + return CMD_SUCCESS; +} + +/* 显示 DAU 状态. */ +CMD(show_prps, + show_prps_cmd, + "show prps", + "Show\n" + "PRPS\n") +{ + return CMD_SUCCESS; + pd_prps_show(); + + return CMD_SUCCESS; +} + +/* 显示 DAU 状态. */ +CMD(show_pd, + show_pd_cmd, + "show pd", + "Show\n" + "Partial discharge\n") +{ + return CMD_SUCCESS; + pd_show(); + + return CMD_SUCCESS; +} + +/* 配置保存函数. */ +int _pd_config_save(vty_t* vty) +{ + int16_t i = 0; + + vty_out(vty, "trend-interval %d%s", pd_config.config.trend_period, VTY_NEWLINE); + i++; + + vty_out(vty, "real-interval %d%s", pd_config.config.real_period, VTY_NEWLINE); + i++; + + vty_out(vty, "heartbeat-interval %d%s", pd_config.config.heartbeat_period, VTY_NEWLINE); + i++; + + switch(pd_config.config.sync_mode) + { + case PD_SYNC_PT: + vty_out(vty, "sync-mode pt%s", VTY_NEWLINE); + i++; + break; + case PD_SYNC_POWER: + vty_out(vty, "sync-mode power%s", VTY_NEWLINE); + i++; + break; + case PD_SYNC_OUTSIDE: + vty_out(vty, "sync-mode outside%s", VTY_NEWLINE); + i++; + break; + default: + break; + } + + return i; +} + +/* config模式配置保存函数: vty -- 相应的终端 */ +int32_t _pd_port_config_save(vty_t *vty) +{ + array_t *configs = pd_port_node.configs; + pd_port_cmd_save_config_f *func = NULL; + uint8_t i = 0; + uint8_t unit = 0; + uint8_t port = 0; + uint16_t field = 0; + + /* 其他配置保存 */ + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + if (!dau[unit]->is_valid) + { + continue; + } + + for(port = 0; port < dau[unit]->port_num; port++) + { + vty_out(vty, "interface port %d/%d%s", unit + 1, port + 1, VTY_NEWLINE); + + for(i = 0; i < array_active(configs); i++) + { + func = array_lookup(configs, i); + if (!func) + { + continue; + } + + func(vty, unit, port); + } + + field = pd_config.port_config[unit][port].config.sensor_type; + vty_out(vty, " sensor-type %s%s", pd_sen_type_str[field], VTY_NEWLINE); + vty_out(vty, " noise-reduction %d%s", pd_config.port_config[unit][port].config.noise_reduction, VTY_NEWLINE); + vty_out(vty, " noise-env %d%s", pd_config.port_config[unit][port].config.env_noise, VTY_NEWLINE); + vty_out(vty, " evnet-thr %d%s", pd_config.port_config[unit][port].config.event_threshold, VTY_NEWLINE); + vty_out(vty, " evnet-cnt %d%s", pd_config.port_config[unit][port].config.event_counter, VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + } + } + + return E_NONE; +} + +/* 将端口字符串, 转换成 unit port 格式. */ +int32_t _pd_port_str_to_unit_port(const char *port_str, uint8_t *unit, uint8_t *port) +{ + char *str = NULL; + char *p = NULL; + char temp[8]; + uint8_t len = 0; + uint8_t i = 0; + + snprintf(temp, 8, "%s", port_str); + str = strtok_r(temp, "/", &p); + while(str != NULL) + { + /* 检查长度. */ + if (len >= 2) + { + return E_BAD_PARAM; + } + + /* 检查字符串 */ + for(i = 0; str[i] && str[i] != '\0'; i++) + { + if (!(str[i] >= '0' && str[i] <= '9')) + { + return E_BAD_PARAM; + } + } + + /* 检查数据长度 */ + if (i != 1) + { + return E_BAD_PARAM; + } + + /* 读取板卡和端口号. */ + if (0 == len) + { + *unit = strtol(str, NULL, 10) - 1; + } + else + { + *port = strtol(str, NULL, 10) - 1; + } + len++; + + /* 获取下个数据 */ + str = strtok_r(NULL, "/", &p); + } + + return E_NONE; +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 局放程序入口函数. + param: + return: */ +int32_t pd_main(void) +{ + uint8_t i = 0; + uint8_t j = 0; + int32_t rv = 0; + +#if 0 + /* 初始化基本参数. */ + for(i = 0; i < PD_DAU_SUM; i++) + { + for(j = 0; j < PD_DAU_PORT_SUM; j++) + { + pd_config.port_config[i][j].config.sensor_type = PD_SEN_TYPE_SIG; + pd_config.port_config[i][j].config.filter = CSG_FILTER_TYPE_FR; + pd_config.port_config[i][j].config.phase_sequence = 1; + + pd_config.port_config[i][j].config.event_threshold = 400; + pd_config.port_config[i][j].config.event_counter = 10; + pd_config.port_config[i][j].config.env_noise = 100; + pd_config.port_config[i][j].config.noise_reduction = 30; + + pd_config.port_config[i][j].config.sample_rate = 50000000; + pd_config.port_config[i][j].config.storage_event = 1000; + pd_config.port_config[i][j].config.storage_wave = 1000; + } + } + + pd_config.config.sync_mode = PD_SYNC_OUTSIDE; + pd_config.config.real_period = 1; + pd_config.config.storage_real = 1000; + pd_config.config.trend_period = 1; + pd_config.config.storage_trend = 1000; + pd_config.config.heartbeat_period = 1; + + pd_config.config.limit_event_time = 300; + pd_config.config.limit_event_cnt = 5; + pd_config.config.limit_event_interval = 60; + + /* 注册配置保存函数 */ + rv = cmd_config_node_config_register(CONFIG_PRI_PD, _pd_config_save); + if (rv != E_NONE) + { + log_err(LOG_PD, "Command save register ERROR %d!", rv); + return rv; + } +#endif + + /* 注册端口节点. */ + cmd_install_node(&pd_port_node, _pd_port_config_save); + pd_port_node.prompt = XMALLOC(MTYPE_DAU, PD_PORT_PROMPT_LEN); + pd_port_node.configs = array_init(PD_PORT_CMD_PRI_COUNT, MTYPE_DAU); + + cmd_install_element(CONFIG_NODE, &pd_real_interval_cmd); + cmd_install_element(CONFIG_NODE, &pd_trend_interval_cmd); + cmd_install_element(CONFIG_NODE, &pd_heartbeat_interval_cmd); + cmd_install_element(CONFIG_NODE, &pd_sync_mode_cmd); + + cmd_install_element(CONFIG_NODE, &pd_port_terminal_cmd); + cmd_install_element(PORT_NODE, &pd_port_terminal_cmd); + cmd_install_element(PORT_NODE, &pd_sensor_type_cmd); + cmd_install_element(PORT_NODE, &pd_noise_reduction_cmd); + cmd_install_element(PORT_NODE, &pd_noise_env_cmd); + cmd_install_element(PORT_NODE, &pd_evnet_thr_cmd); + cmd_install_element(PORT_NODE, &pd_evnet_cnt_cmd); + + cmd_install_element(COMMON_NODE, &show_prps_cmd); + cmd_install_element(COMMON_NODE, &show_pd_cmd); + + //LD_E_RETURN(DBG_M_PD_ERR, log_handle_init()); + LD_E_RETURN(DBG_M_PD_ERR, csg_handle_init()); + LD_E_RETURN(DBG_M_PD_ERR, dau_handle_init()); + //LD_E_RETURN(DBG_M_PD_ERR, localstorage_handle_init()); + //LD_E_RETURN(DBG_M_PD_ERR, debug_handle_init()); + + return E_NONE; +} + +int32_t pd_port_cmd_config_register(int32_t pri, pd_port_cmd_save_config_f *func) +{ + cmd_node_t *node = &pd_port_node; + + /* 参数检查 */ + if (pri >= PD_PORT_CMD_PRI_COUNT || !func) + { + return E_BAD_PARAM; + } + + /* 加入列表 */ + array_set(node->configs, pri, func, MTYPE_DAU); + + return 0; +} + +void pd_sync_mode_set(void) +{ + uint16_t temp = 0; + + if (PD_SYNC_OUTSIDE == pd_config.config.sync_mode) + { + temp = CPLD_SYNC_OUTSIDE; + } + else + { + temp = CPLD_SYNC_PT; + } + + cpld_write(CPLD_REG_SYNC, 1, &temp); +} + +void pd_wdg_clr(void) +{ + uint16_t temp = 0; + + temp = 0x01; + cpld_write(CPLD_REG_WDG_CLR, 1, &temp); +} + +void pd_sync_state_get(void) +{ + uint16_t temp = 0; + + cpld_read(CPLD_REG_STAT, 1, &temp); + pd_state.sync = ((temp & 0x2) != 0); +} + +void pd_prps_show(void) +{ + uint8_t unit = 0; + uint8_t port = 0; + pd_port_config_t *config = NULL; + + printh("Concern Bitmap: 0x%x\r\n", pd_config.concern_bitmap); + printh("UN PO CO RN AN MN FC\r\n"); + for(unit = 0; unit < PD_DAU_SUM; unit++) + { + if (!dau[unit]) + { + continue; + } + + for(port = 0; port < dau[unit]->port_num; port++) + { + config = &pd_config.port_config[unit][port]; + printh("%-02d %-02d %-02d %-02d %-02d %-02d %-02d\r\n", unit, port, config->is_concern, + config->r_noise_reduction, config->auto_noise_reduction, config->manual_noise_reduction / 10, + config->filter_cfg); + } + } +} + +void pd_show(void) +{ + printh("Synchronization: %s\r\n", pd_state.sync ? "valid" : "invalid"); + printh("Receive count: %d %d\r\n", dau_ctrl.recv_cnt, dau_ctrl.recv_cnt_old); + printh("Receive error count: %d %d\r\n", dau_ctrl.recv_err_cnt, dau_ctrl.recv_err_cnt_old); + printh("Data error count: %d %d\r\n", dau_ctrl.data_err_cnt, dau_ctrl.data_err_cnt_old); + printh("Send error count: %d %d\r\n\n", dau_ctrl.send_err_cnt, dau_ctrl.send_err_cnt_old); +} +#else +int32_t PD_main(void) +{ + return 0; +} +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/lib/a_process/pd_storage.c b/app/lib/a_process/pd_storage.c new file mode 100644 index 0000000..7d1d1c7 --- /dev/null +++ b/app/lib/a_process/pd_storage.c @@ -0,0 +1,567 @@ +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 用户代码头文件. */ +#include "cmd.h" +#include "fifo.h" +#include "pd_main.h" +#include "pd_dau.h" +#include "pd_storage.h" + +/* Private define ------------------------------------------------------------*/ +#define STORAGE_PATH "/run/media/mmcblk1p3/Data" +#define STORAGE_PATH_EVENT "/run/media/mmcblk1p3/Data/Event" +#define STORAGE_PATH_TREND "/run/media/mmcblk1p3/Data/Trend" +#define STORAGE_PATH_FIXEDTIMEPRPS "/run/media/mmcblk1p3/Data/FixedTimePRPS" + +#define DEL_FILE_RATE_DEFAULT 10 + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +storage_t storage; +storage_arg_t storage_arg; + +/* Private function prototypes -----------------------------------------------*/ + + +/* Internal functions --------------------------------------------------------*/ + +/* 以“wb”方式覆盖写入二进制文件 */ +int32_t _storage_write_hex_file( const char *file_name, uint8_t *data, uint32_t len) +{ + if (file_name == NULL || data == NULL || len < 1) + { + log_err( LOG_STORAGE, "Write file %s param ERROR!", file_name ); + return E_BAD_PARAM; + } + + usleep(1000); + FILE *file = fopen( file_name, "wb" ); + if (file == NULL) + { + log_err( LOG_STORAGE, "Write file %s ERROR!", file_name ); + return E_ERROR; + } + usleep(1000); + + int32_t bytes_cnt; + bytes_cnt = fwrite( data, len, 1, file ); + fclose( file ); + return bytes_cnt; +} + +/* 将文件名解析为UTC索引号 */ +uint32_t _storage_filename_parse_utc( char *file_name ) +{ + uint32_t file_utc; + char date[FILE_NAME_LEN] = {0}; + char time[FILE_NAME_LEN] = {0}; + + strncpy( date, file_name, 10 ); + strncpy( time, file_name + 11, 8 ); + + time_str_to_long( date, time, &file_utc); + + return file_utc; +} + +/* 以UTC时间作为文件名 */ +int32_t _storage_set_filename( char *file_name, uint32_t file_utc ) +{ + char time_str[FILE_NAME_LEN]; + + time_t file_time = file_utc; + struct tm *lt = localtime( &file_time ); + + strftime( time_str, FILE_NAME_LEN, "%Y-%m-%d %H:%M:%S", lt ); + snprintf( file_name, FILE_NAME_LEN, "%s.dat", time_str ); + + return E_NONE; +} + +/* 根据UTC的最大、最小值以及比重参数rate确定UTC基准值,UTC小于该基准值的文件将被删除 */ +uint32_t _storage_get_utc_base( uint32_t max_utc, uint32_t min_utc, int rate) +{ + uint32_t utc_base; + utc_base = (max_utc - min_utc) / rate + min_utc; + return utc_base; +} + +/* 创建存储文件夹 */ +int32_t _storage_create_dir( void ) +{ + uint8_t unit; + uint8_t port; + char dir_path[DIR_PATH_LEN] = {0}; + + /* 创建父文件夹 */ + for (int i = 0; i < 4; i++) + { + switch( i ) + { + case 0: + snprintf( dir_path, DIR_PATH_LEN, "%s", STORAGE_PATH ); + break; + case 1: + snprintf( dir_path, DIR_PATH_LEN, "%s", STORAGE_PATH_EVENT ); + break; + case 2: + snprintf( dir_path, DIR_PATH_LEN, "%s", STORAGE_PATH_TREND ); + break; + case 3: + snprintf( dir_path, DIR_PATH_LEN, "%s", STORAGE_PATH_FIXEDTIMEPRPS ); + break; + default: + return E_NONE; + } + if( access( dir_path, 0 ) == -1 ) + { + if (mkdir( dir_path, 0777 )) + { + log_err( LOG_STORAGE, "Create storage DIR %s failed!", dir_path ); + return E_ERROR; + } + } + } + + /* 创建事件数据通道子文件夹 */ + for (unit = 0; unit < PD_DAU_SUM; unit++) + { + for (port = 0; port < PD_DAU_PORT_SUM; port++) + { + snprintf( dir_path, DIR_PATH_LEN, "%s/%02d", STORAGE_PATH_EVENT, (unit * PD_DAU_PORT_SUM + port + 1) ); + if( access( dir_path, 0 ) == -1 ) + { + if (mkdir( dir_path, 0777 )) + { + log_err( LOG_STORAGE, "Create storage DIR %s failed!", dir_path ); + return E_ERROR; + } + } + } + } + + return E_NONE; +} + +/* 遍历文件夹以获得最小UTC值与文件数量 */ +int32_t _storage_get_min_utc_file_cnt( char *dir_path, uint32_t *min_utc, uint16_t *file_count ) +{ + *file_count = 0; + *min_utc = time(NULL); + + if (access( dir_path, 0 ) == -1) //文件夹不存在 + { + if (mkdir( dir_path, 0777 )) + { + log_err( LOG_STORAGE, "Create storage DIR %s failed!", dir_path ); + return E_ERROR; + } + return E_NONE; + } + + DIR *dir; + struct dirent *ptr; + uint32_t file_utc = 0; + + if( (dir = opendir( dir_path )) == NULL ) + { + log_err( LOG_STORAGE, "ERROR! Can't open dir %s to get param!", dir_path ); + return E_ERROR; + } + while( (ptr = readdir( dir )) != NULL ) + { + if (ptr->d_type == DT_REG) //常规文件 + { + file_utc = _storage_filename_parse_utc( ptr->d_name ); + if (*min_utc > file_utc) + { + *min_utc = file_utc; + } + (*file_count)++; + } + } + closedir( dir ); + return E_NONE; +} + +/* 存储参数初始化,包括已存储文件数,UTC的最小值等 */ +int32_t _storage_arg_init( void ) +{ + /* 事件存储参数初始化 */ + uint8_t unit; + uint8_t port; + for (unit = 0; unit < PD_DAU_SUM; unit++) + { + for (port = 0; port < PD_DAU_PORT_SUM; port++) + { + /* 判断事件频繁参数初始化 */ + storage_arg.utc_base[unit][port] = time(NULL); + storage_arg.limit_cnt[unit][port] = pd_config.config.limit_event_cnt; + + char dir_path[DIR_PATH_LEN] = {0}; + snprintf( dir_path, DIR_PATH_LEN, "%s/%02d", STORAGE_PATH_EVENT, (unit * PD_DAU_PORT_SUM + port + 1) ); + _storage_get_min_utc_file_cnt( dir_path, &storage_arg.event_min_utc[unit][port], &storage_arg.event_cnt[unit][port] ); + } + } + + /* 趋势存储参数初始化 */ + _storage_get_min_utc_file_cnt( STORAGE_PATH_TREND, &storage_arg.trend_min_utc, &storage_arg.trend_cnt ); + + /* 定时存储参数初始化 */ + _storage_get_min_utc_file_cnt( STORAGE_PATH_FIXEDTIMEPRPS, &storage_arg.fixedtimePRPS_min_utc, &storage_arg.fixedtimePRPS_cnt ); + + return E_NONE; +} + +/* 运行中无法访问文件夹,说明文件夹可能被删除,重新创建 */ +int32_t _storage_recreate_dir( char *dir_path ) +{ + /* 若创建不成功,说明父文件夹可能被删除,此时从父文件夹开始创建并重新初始化参数 */ + if (mkdir( dir_path, 0777 )) + { + _storage_create_dir(); + _storage_arg_init(); + return E_NONE; + } + return TRUE; //表示文件夹创建成功 +} + +/** + * @brief 删除文件函数 + * @param *dir_path: 进行删除操作的文件夹 + * @param utc_base: UTC基准值,UTC小于等于该值的文件将被删除 + * @param *min_utc: 引用全局变量,最小UTC值,最终结果为文件夹剩余文件中的最小UTC值 + * @param *file_count: 引用全局变量,文件总数,最终结果为文件夹中剩余文件数量 +*/ +int32_t _storage_delete_file( char *dir_path, uint32_t utc_base, uint32_t *min_utc, uint16_t *file_count ) +{ + DIR *dir; + struct dirent *ptr; + char remove_path[DIR_PATH_LEN]; //删除文件的路径 + int remove_ret; //remove函数的返回值 + uint32_t file_utc; + *min_utc = time(NULL); + + if( (dir = opendir( dir_path )) == NULL ) + { + log_err( LOG_STORAGE, "ERROR! Can't open delete dir %s !", dir_path ); + return E_ERROR; + } + while( (ptr = readdir( dir )) != NULL ) + { + if (ptr->d_type == DT_REG) //常规文件 + { + file_utc = _storage_filename_parse_utc( ptr->d_name ); + + if( (file_utc > utc_base) && (file_utc < *min_utc) ) + { + *min_utc = file_utc; + } + + if (file_utc <= utc_base) //需要被删除的文件 + { + memset( remove_path, '\0', sizeof(remove_path) ); + strcpy( remove_path, dir_path ); + strcat( remove_path, "/" ); + strcat( remove_path, ptr->d_name ); + + remove_ret = remove( remove_path ); + if (remove_ret == 0) //成功删除,文件总数减一 + { + (*file_count)--; + } + else if (remove_ret == -1) + { + log_warn( LOG_STORAGE, "WARN! Remove file %s failed!", ptr->d_name ); + + if (errno == EROFS) + { + printf( "File Only Read\r\n" ); + } + else if (errno == EFAULT) + { + printf( "File Pointer to the overflow\r\n" ); + } + else if (errno == ENAMETOOLONG) + { + printf( "File Name Over Long\r\n" ); + } + } + } + } + } + closedir( dir ); + return E_NONE; +} + +/* 判断时间存储是否过于频繁,返回E_NONE代表将存储该文件 */ +int32_t _storage_event_frequently( uint32_t utc, uint8_t unit, uint8_t port ) +{ + /* 周期结束,判断是否频繁存储 */ + if( storage_arg.limit_cnt[unit][port] <= 0 ) + { + /* 上周期存储频繁,间隔存储直到限制时间到达 */ + if( utc < (storage_arg.utc_base[unit][port] + pd_config.config.limit_event_time) ) + { + /* 间隔存储 */ + if( utc < (storage_arg.event_max_utc[unit][port] + pd_config.config.limit_event_interval) ) + { + return E_TIMEOUT; + } + else + { + return E_NONE; + } + } + /* 间隔存储限时结束或上周期存储不频繁 */ + else + { + storage_arg.limit_cnt[unit][port] = pd_config.config.limit_event_cnt - 1; + storage_arg.utc_base[unit][port] = utc; + return E_NONE; + } + } + /* 周期未结束 */ + storage_arg.limit_cnt[unit][port]--; + return E_NONE; +} + +/* 事件文件存储函数 */ +int32_t _storage_event( pd_event_t *data ) +{ + uint8_t unit; + uint8_t port; + dau_vport_to_port( data->vport, &unit, &port ); + + if (_storage_event_frequently( data->utc, unit, port )) + { + return E_NONE; + } + + int32_t bytes_cnt; + char file_name[FILE_NAME_LEN]; + char dir_path[DIR_PATH_LEN]; + char dir_file_write[DIR_PATH_LEN]; + + _storage_set_filename( file_name, data->utc ); + + snprintf( dir_path, DIR_PATH_LEN, "%s/%02d", STORAGE_PATH_EVENT, data->vport ); + if (access( dir_path, 0 ) == -1) //文件夹不存在或被删除 + { + if (_storage_recreate_dir( dir_path )) + { + storage_arg.event_min_utc[unit][port] = data->utc; + storage_arg.event_cnt[unit][port] = 0; + } + } + + snprintf( dir_file_write, DIR_PATH_LEN, "%s/%s", dir_path, file_name ); + bytes_cnt = _storage_write_hex_file( dir_file_write, (uint8_t*) data, sizeof(pd_event_t) ); + if (bytes_cnt < 0) + { + DBG( DBG_M_STORAGE_ERR, "Local storage return %s!\r\n", safe_strerror(errno) ); + return E_ERROR; + } + + storage_arg.event_max_utc[unit][port] = data->utc; + storage_arg.event_cnt[unit][port]++; + if (storage_arg.event_cnt[unit][port] >= pd_config.port_config[unit][port].config.storage_event) + { + _storage_delete_file( dir_path, + _storage_get_utc_base( storage_arg.event_max_utc[unit][port], storage_arg.event_min_utc[unit][port], DEL_FILE_RATE_DEFAULT ), + &storage_arg.event_min_utc[unit][port], + &storage_arg.event_cnt[unit][port] ); + } + + return E_NONE; +} + +/* 趋势文件存储函数 */ +int32_t _storage_trend( pd_trend_t *data ) +{ + int32_t bytes_cnt; + char file_name[FILE_NAME_LEN]; + char dir_path[DIR_PATH_LEN]; + char dir_file_write[DIR_PATH_LEN]; + + _storage_set_filename( file_name, data->utc ); + + snprintf( dir_path, DIR_PATH_LEN, "%s", STORAGE_PATH_TREND ); + if (access( dir_path, 0 ) == -1) //文件夹不存在 + { + if (_storage_recreate_dir( dir_path )) + { + storage_arg.trend_min_utc = data->utc; + storage_arg.trend_cnt = 0; + } + } + + snprintf( dir_file_write, DIR_PATH_LEN, "%s/%s", dir_path, file_name ); + bytes_cnt = _storage_write_hex_file( dir_file_write, (uint8_t*) data, sizeof(pd_trend_t) ); + if (bytes_cnt < 0) + { + DBG( DBG_M_STORAGE_ERR, "Local storage return %s!\r\n", safe_strerror(errno) ); + return E_ERROR; + } + + storage_arg.trend_max_utc = data->utc; + storage_arg.trend_cnt++; + if (storage_arg.trend_cnt >= pd_config.config.storage_trend) + { + _storage_delete_file( dir_path, + _storage_get_utc_base( storage_arg.trend_max_utc, storage_arg.trend_min_utc, DEL_FILE_RATE_DEFAULT ), + &storage_arg.trend_min_utc, + &storage_arg.trend_cnt ); + } + + return E_NONE; +} + +/* 定时PRPS文件存储函数 */ +int32_t _storage_fixedtimePRPS( pd_prps_t *data ) +{ + int32_t bytes_cnt; + char file_name[FILE_NAME_LEN]; + char dir_path[DIR_PATH_LEN]; + char dir_file_write[DIR_PATH_LEN]; + + _storage_set_filename( file_name, data->utc ); + + snprintf( dir_path, DIR_PATH_LEN, "%s", STORAGE_PATH_FIXEDTIMEPRPS ); + if (access( dir_path, 0 ) == -1) //文件夹不存在 + { + if (_storage_recreate_dir( dir_path )) + { + storage_arg.fixedtimePRPS_min_utc = data->utc; + storage_arg.fixedtimePRPS_cnt = 0; + } + } + + snprintf( dir_file_write, DIR_PATH_LEN, "%s/%s", dir_path, file_name ); + bytes_cnt = _storage_write_hex_file( dir_file_write, (uint8_t*) data, sizeof(pd_prps_t) ); + if (bytes_cnt < 0) + { + DBG( DBG_M_STORAGE_ERR, "Local storage return %s!\r\n", safe_strerror(errno) ); + return E_ERROR; + } + + storage_arg.fixedtimePRPS_max_utc = data->utc; + storage_arg.fixedtimePRPS_cnt++; + if (storage_arg.fixedtimePRPS_cnt >= pd_config.config.storage_real) + { + _storage_delete_file( dir_path, + _storage_get_utc_base( storage_arg.fixedtimePRPS_max_utc, storage_arg.fixedtimePRPS_min_utc, DEL_FILE_RATE_DEFAULT ), + &storage_arg.fixedtimePRPS_min_utc, + &storage_arg.fixedtimePRPS_cnt ); + } + + return E_NONE; +} + +/* 本地存储处理句柄 */ +void *_local_storage_handle(void *arg) +{ + pd_storage_msg_t *recv_msg = NULL; + + while(1) + { + if(fifo_read(storage.storage_fifo_id,(void **)&recv_msg) != 0) + { + DBG(DBG_M_STORAGE_ERR,"ERROR at fifo %d read!\r\n",storage.storage_fifo_id); + continue; + } + + /* 数据存储处理 */ + switch(recv_msg->type) + { + case STORAGE_TYPE_EVENT: + _storage_event( (pd_event_t*) recv_msg->data ); + break; + case STORAGE_TYPE_TREND: + _storage_trend( (pd_trend_t*) recv_msg->data ); + break; + case STORAGE_TYPE_ALARM: + break; + case STORAGE_TYPE_RUNSTATUS: + break; + case STORAGE_TYPE_FIXEDTIMERPRPS: + _storage_fixedtimePRPS( (pd_prps_t*) recv_msg->data ); + break; + default: + break; + } + + /* 释放存储数据内存 */ + XFREE(MTYPE_STORAGE,recv_msg->data); + fifo_push(storage.storage_fifo_id); + } + return NULL; +} + +/* 本地存储公共部分初始化 */ +int32_t _local_storage_init_common(void) +{ + struct sched_param param; + pthread_attr_t attr; + pthread_t pid; + int rv = 0; + + _storage_create_dir(); + _storage_arg_init(); + + storage.storage_fifo_id = fifo_create(STORAGE_DATA_FIFO, 32); + if(storage.storage_fifo_id < 0) + { + log_err(LOG_STORAGE,"Open fifo " STORAGE_DATA_FIFO " error!"); + return E_ERROR; + } + + pthread_mutex_init(&storage.mutex, NULL); + + /* 配置线程RR调度,优先级50 */ + pthread_attr_init(&attr); + param.sched_priority = 50; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + rv = pthread_create(&pid, &attr, _local_storage_handle, NULL); + if(rv != 0) + { + log_err(LOG_STORAGE, "PD can't create storage pthread %d!", rv); + return E_SYS_CALL; + } + else + { + thread_m_add("LOCAL_STORAGE", pid); + } + pthread_attr_destroy(&attr); + + return E_NONE; +} + +/* 本地存储模块初始化 */ +int32_t localstorage_handle_init(void) +{ + /* 初始化模块 */ + LD_E_RETURN(DBG_M_STORAGE_ERR,_local_storage_init_common()); + return E_NONE; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ \ No newline at end of file diff --git a/app/lib/a_process/process.c b/app/lib/a_process/process.c new file mode 100755 index 0000000..edfd6dc --- /dev/null +++ b/app/lib/a_process/process.c @@ -0,0 +1,260 @@ +/***************************************************************************** + * file lib/process/process.c + * author YuLiang + * version 1.0.0 + * date 26-Sep-2021 + * brief This file provides all the process related operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "mtimer.h" +#include "process.h" +#include "hwgpio.h" +#include "fifo.h" + +#include "pd_main.h" +#include "pd_dau.h" +#include "pd_cpld.h" + +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +int32_t recv_qid; +uint16_t version_hex; +uint32_t start_time; + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +/* 信号处理函数 */ +void _signal_handler(int sig) +{ + if (SIGSEGV == sig) + { + log_backtrace(LOG_LVL_ERR); + } + //else if(SIGINT == sig + // || SIGTSTP == sig) + //{ + /* 屏蔽信号 */ + // return; + //} + + exit(-1); +} + +/* 看门狗初始化. */ +void _wdg_init(void) +{ + uint16_t temp = 0; + + /* 初始化看门狗计数器, 单位 ms. */ + temp = 0xffff; + cpld_write(CPLD_REG_WDG_TM, 1, &temp); + temp = 0x01; + cpld_write(CPLD_REG_WDG_EN, 1, &temp); + cpld_write(CPLD_REG_WDG_CLR, 1, &temp); +} + +/* 喂狗. */ +void _wdg_clr(void) +{ + uint16_t temp = 0; + + /* 喂狗, 1 - 喂狗. */ + temp = 0x01; + cpld_write(CPLD_REG_WDG_CLR, 1, &temp); +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 数据处理初始化. + param: + return: (E_NONE)成功,(其他)失败 */ +int32_t process_init(void) +{ +#if 0 + /* 创建消息队列. */ + if ((recv_qid = msgget(0x4321, IPC_CREAT | 0666)) == -1) + { + log_err(LOG_DEFAULT, "message ERROR at msgget return %s!", safe_strerror(errno)); + } + + /* 清空消息队列. */ + msgctl(recv_qid, IPC_RMID, NULL); + if ((recv_qid = msgget(0x4321, IPC_CREAT | 0666)) == -1) + { + log_err(LOG_DEFAULT, "message ERROR at msgget return %s!", safe_strerror(errno)); + } +#endif + + /* 初始化局放应用. */ + pd_main(); + + return E_NONE; +} + +/* description: 程序入口函数. + param: + return: */ +int32_t main(int32_t argc, char **argv) +{ + struct sigaction act; + uint32_t cnt = 0; + + /* 设置本地化信息为默认值. */ + setlocale(LC_ALL, ""); + + /* 必须最前面, 初始化内存管理模块的基本参数. */ + mtype_init_befor(); + + /* log初始化 */ + //log_open(); + /* 初始化时定量清除 log */ + //log_clean(); + //log_out(LOG_DEFAULT, LOG_LVL_WARN, "System start!"); + + /* 设置信号处理的回调函数 */ + act.sa_handler = _signal_handler; + sigemptyset(&act.sa_mask); + sigaction(SIGINT, &act, NULL); + sigaction(SIGSEGV, &act, NULL); + sigaction(SIGTSTP, &act, NULL); + + /* 初始化cli等公共模块. */ + cmd_init(); + thread_m_init(); + mtype_init(); + dbg_init(); + mtimer_init(); + vtysh_init(); + gpio_init(); + fifo_init(); + + /* 初始化 DAU 复位, 并不采集数据. 这里必须等待, 不然 gpio 模拟 spi 会失败. */ + cpld_handle_init(); + + //_wdg_init(); + + //dau_shutdown(); + + /* 主处理函数初始化 */ + process_init(); + + /* 配置恢复命令行启动 */ + //vtysh_config_recovery(); + + //dau_start(); + + /* 启动命令行 */ + vtysh_shell_init(); + + /* 初始化完成 */ + version_hex = version_str_to_int(); + is_system_init = TRUE; + start_time = time(NULL); + //GPIO_ERR1_LED(1); + + /* 启动ssh命令行 */ + vtycmd_init(); + + /* 主循环, 点灯喂狗. */ + for(;;) + { + sleep(1); +#if 0 + /* 获取同步状态, 更新收报计数. */ + if (0 == (cnt & 0x7)) + { + pd_sync_state_get(); + GPIO_SYNC_LED(!pd_state.sync); + if (dau_ctrl.recv_cnt > 2000) + { + printh("ERROR recv_cnt %d\r\n", dau_ctrl.recv_cnt); + } + dau_ctrl.recv_cnt_old = dau_ctrl.recv_cnt; + dau_ctrl.recv_cnt = 0; + } + + /* 点 RUN 灯. */ + if (cnt & 0x1) + { + GPIO_RUN_LED(0); + } + else + { + GPIO_RUN_LED(1); + } + + sleep(1); + cnt++; + + /* 更新数据处理计数. */ + if (0 == (cnt & 0x3f)) + { + dau_ctrl.recv_err_cnt_old = dau_ctrl.recv_err_cnt; + dau_ctrl.recv_err_cnt = 0; + dau_ctrl.data_err_cnt_old = dau_ctrl.data_err_cnt; + dau_ctrl.data_err_cnt = 0; + dau_ctrl.send_err_cnt_old = dau_ctrl.send_err_cnt; + dau_ctrl.send_err_cnt = 0; + } + + /* 喂狗. */ + if (0 == (cnt & 0x1f)) + { + _wdg_clr(); + } +#endif + } + + return 0; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/lib/m_management/array.c b/app/lib/m_management/array.c new file mode 100644 index 0000000..efdf06f --- /dev/null +++ b/app/lib/m_management/array.c @@ -0,0 +1,297 @@ +/***************************************************************************** + * file lib/management/array.c + * author YuLiang + * version 1.0.0 + * date 22-Sep-2021 + * brief This file provides all the array related operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ + +/* 用户代码头文件. */ +#include "array.h" +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +/* 确保设置的数组成员没有内存越界,如果超出数组长度,则重新realloc: + a -- 数组结构 + len -- 需要申请的长度 + mem_type -- 使用的内存模块 + + return: void */ +static void _array_len_ensure(array_t *a, uint32_t len, int32_t mem_type) +{ + if (a->alloced > len) + { + return; + } + + a->index = XREALLOC(mem_type, a->index, sizeof(void *) * (a->alloced * 2)); + memset(&a->index[a->alloced], 0, sizeof(void *) * a->alloced); + a->alloced *= 2; + + if (a->alloced <= len) + { + _array_len_ensure(a, len, mem_type); + } +} + +/* 查找数组a的第一个空位置. + a -- 数组结构 + + return: 第一个空位置索引 */ +uint32_t _array_empty_slot(array_t *a) +{ + uint32_t i = 0; + + for(i = 0; i < a->active; i++) + { + if (NULL == a->index[i]) + { + return i; + } + } + + return i; +} + +/* Interface functions -------------------------------------------------------*/ +/* 初始化数组: + size -- 数组初始大小 + mem_type -- 使用的内存模块 + + return: 数组指针 */ +array_t *array_init(uint32_t size, int32_t mem_type) +{ + array_t *a = XMALLOC(mem_type, sizeof(array_t)); + + /* 如果大小为0,设置默认大小. */ + if (0 == size) + { + size = ARRAY_MIN_SIZE; + } + + a->alloced = size; + a->active = 0; + a->index = XMALLOC(mem_type, sizeof(void *) * size); + + return a; +} + +/* 设置数组相应索引位置的值: + a -- 数组 + i -- 索引 + val -- 值 + mem_type -- 使用的内存模块 + + return: void */ +void array_set(array_t *a, uint32_t i, void *val, int32_t mem_type) +{ + _array_len_ensure(a, i, mem_type); + + a->index[i] = val; + + if (a->active <= i) + { + a->active = i + 1; + } +} + +/* 删除数组相应索引位置的值: + a -- 数组 + i -- 索引 + + return: void */ +void array_unset(array_t *a, uint32_t i) +{ + if (i >= a->alloced) + { + return; + } + + a->index[i] = NULL; + + /* 不是最后一个元素直接返回. */ + if (i + 1 != a->active) + { + return; + } + + /* 如果是最后一个元素,则将当前可使用指针前移. */ + while(--a->active) + { + if (a->index[a->active - 1] != NULL) + { + break; + } + } +} + +/* 将数据放入数组的第一个空位置: + a -- 数组 + val -- 值 + mem_type -- 使用的内存模块 + + return: 数据放入位置的索引值 */ +uint32_t array_append(array_t *a, void *val, int32_t mem_type) +{ + uint32_t i = 0; + + i = _array_empty_slot(a); + _array_len_ensure(a, i, mem_type); + + a->index[i] = val; + + if (a->active <= i) + { + a->active = i + 1; + } + + return i; +} + +/* 复制数组a: + a -- 数组 + mem_type -- 使用的内存模块 + + return: 新数组指针 */ +array_t *array_copy(array_t *a, int32_t mem_type) +{ + unsigned int size = 0; + array_t *array = XMALLOC(mem_type, sizeof(array_t)); + + array->active = a->active; + array->alloced = a->alloced; + + size = sizeof(void *) * (a->alloced); + array->index = XMALLOC(mem_type, size); + memcpy(array->index, a->index, size); + + return array; +} + +/* 将数组a和数组b合并成数组a: + a -- 数组1 + b -- 数组2 + mem_type -- 使用的内存模块 + + return: void */ +void array_merge(array_t *a, array_t *b, int32_t mem_type) +{ + uint32_t size = 0; + + if (0 == b->alloced) + { + return; + } + + size = sizeof(void *) * (a->alloced + b->alloced); + a->index = XREALLOC(mem_type, a->index, size); + memcpy(&a->index[a->active], b->index, sizeof(void *) * b->active); + + a->active = a->active + b->active; + a->alloced = a->alloced + b->alloced; +} + +/* 释放array_t: + a -- 数组 + mem_type -- 使用的内存模块 + + return: void */ +void array_free_wrapper(array_t *a, int32_t mem_type) +{ + XFREE(mem_type, a); +} + +/* 释放array_t->index: + a -- 数组 + mem_type -- 使用的内存模块 + + return: void */ +void array_free_index(array_t *a, int32_t mem_type) +{ + XFREE(mem_type, a->index); +} + +/* 释放array_t和array_t->index: + a -- 数组 + mem_type -- 使用的内存模块 + + return: void */ +void array_free(array_t *a, int32_t mem_type) +{ + XFREE(mem_type, a->index); + XFREE(mem_type, a); +} + +/* 查询数组a的数据i: + a -- 数组 + i -- 元素索引 + + return: 元素指针 */ +void *array_lookup(array_t *a, uint32_t i) +{ + return (i >= a->active) ? NULL : a->index[i]; +} + +/* 获取数组a中有效数据的个数: + a -- 数组 + + return: void */ +uint32_t array_count(array_t *a) +{ + uint32_t i = 0; + uint32_t count = 0; + + for (i = 0; i < a->active; i++) + { + if (a->index[i] != NULL) + { + count++; + } + } + + return count; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/lib/m_management/better_log.c b/app/lib/m_management/better_log.c new file mode 100644 index 0000000..d7c4a53 --- /dev/null +++ b/app/lib/m_management/better_log.c @@ -0,0 +1,555 @@ +/****************************************************************************** + * file lib/management/better_log.c + * author YuLiang + * version 1.0.0 + * date 14-Sep-2021 + * brief This file provides all the log operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ +#include +#include +#include +#include + +/* 用户代码头文件. */ +#include + +/* Private define ------------------------------------------------------------*/ + +#define LOG_LOCK pthread_mutex_lock( &log_sys->log_mutex ) +#define LOG_UNLOCK pthread_mutex_unlock( &log_sys->log_mutex ) + +#define LOG_OUT_LOCK pthread_mutex_lock( &log_sys->log_out_mutex ) +#define LOG_OUT_UNLOCK pthread_mutex_unlock( &log_sys->log_out_mutex ) + +#define LOG_DB_LOCK pthread_mutex_lock(&log_sys->log_db_mutex) +#define LOG_DB_UNLOCK pthread_mutex_unlock(&log_sys->log_db_mutex) + +#define LOG_OUT 0 +#define LOG_SHOW 1 +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +_log_fifo_t log_fifo; +log_sys_t *log_sys; +_log_out_t *log_out_struct; +_log_show_t *log_show_struct; + +static char *log_str = NULL; +static char *log_out_va_str = NULL; +static char *log_fifo_va_str = NULL; + +/* log等级. */ +static const log_lvl_t log_priority[] = +{ + {LOG_LVL_ERR, "err"}, + {LOG_LVL_WARN, "warn"}, + {LOG_LVL_NOTIF, "notif"}, + {LOG_LVL_INFO, "info"}, + {LOG_LVL_DBG, "debug"}, + {-1, NULL} +}; + +/* log模块. */ +static const log_module_t log_module_names[] = +{ + {LOG_DEFAULT, "DEFAULT"}, + {LOG_CLI, "CLI"}, + {LOG_MEMORY, "MEMORY"}, + {LOG_FIFO, "FIFO"}, + {LOG_GPIO, "GPIO"}, + {LOG_PD, "PD"}, + {LOG_DAU, "DAU"}, + {LOG_CSG, "CSG"}, + {LOG_STORAGE, "STORAGE"}, + {LOG_DEBUG, "DEBUG"}, + {-1, NULL} +}; + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ + +/* 文件log输出底层函数. */ +static void _log_out_db( LOG_MODULE_E module, LOG_LVL_E priority, const char *va_str ) +{ + log_sys_t *log = log_sys; + + char time_str[TIME_STR_LEN] = {0}; + char *zErrMsg = NULL; + static uint64_t logCnt = 0; + time_string( log->timestamp_precision, time_str, sizeof(time_str) ); + + LOG_DB_LOCK; + + memset( log_str, 0, LOG_STR_LEN ); + snprintf( log_str, LOG_STR_LEN, + "INSERT INTO better_log (LEVEL,LOG) VALUES (%d, \"%s %s-%s: %s\"); ", + priority, time_str, log_priority[priority].describe, log_module_names[module].describe, va_str ); //id 已设为自增 + + if (sqlite3_exec( log->db, log_str, 0, 0, &zErrMsg ) != SQLITE_OK) + { + printh("SQL INSERT error: %s\r\n", zErrMsg); + sqlite3_free(zErrMsg); + } + + /*定量清除log*/ + logCnt++; + if (logCnt >= 100) + { + clock_t begin,end; + begin = clock(); + log_clean(); + end = clock(); + printh("-------------------log_clean RunTimer : %lf----------------\r\n",(double)(end-begin)/CLOCKS_PER_SEC); + printh("-------------------log_clean RunTimer : %lf----------------\r\n",(double)(end-begin)/CLOCKS_PER_SEC); + printh("-------------------log_clean RunTimer : %lf----------------\r\n",(double)(end-begin)/CLOCKS_PER_SEC); + logCnt = 0; + } + + LOG_DB_UNLOCK; +} + +/* 解日志输出fifo数据封装 */ +static void _log_out( _log_out_t *data ) +{ + _log_out_db( data->module, data->lvl, data->log_out_str ); +} + +/* 串口日志输出 */ +static void _log_out_std( LOG_MODULE_E module, LOG_LVL_E priority, const char *va_str ) +{ + log_sys_t *log = log_sys; + + char time_str[TIME_STR_LEN] = {0}; + time_string(log->timestamp_precision, time_str, sizeof(time_str)); + + printh("%s %s-%s: %s\n", time_str, log_priority[priority].describe, log_module_names[module].describe, va_str); +} + +/* 将日志按照格式串口打印 */ +static int _log_show_print( void *data, int argc, char **argv, char **azColName ) +{ + printh("LOG id %s: %s\r\n", argv[0], argv[2]); + + return 0; +} + +/* log打印底层函数 */ +static void _log_show( _log_show_t *data ) +{ + log_sys_t *log = log_sys; + char *zErrMsg = NULL; + + LOG_DB_LOCK; + + memset( log_str, 0, LOG_STR_LEN ); + switch (data->type) + { + case LOG_SHOW_CNT: //打印指定数量的日志 + snprintf( log_str, LOG_STR_LEN, "SELECT * FROM better_log ORDER BY id DESC LIMIT %d;", data->param ); + break; + + case LOG_SHOW_LVL: //打印指定等级的日志 + snprintf( log_str, LOG_STR_LEN, "SELECT * FROM better_log WHERE LEVEL = %d ORDER BY id DESC LIMIT 100;", data->param ); + break; + + case LOG_SHOW_KEYWORD: //打印含关键词的日志 + if (data->param <= 0) + { + snprintf( log_str, LOG_STR_LEN, "SELECT * FROM better_log WHERE LOG GLOB \'*%s*\' ORDER BY id DESC;", + data->log_show_str ); + } + else + { + snprintf( log_str, LOG_STR_LEN, "SELECT * FROM better_log WHERE LOG GLOB \'*%s*\' ORDER BY id DESC LIMIT %d;", + data->log_show_str, data->param ); + } + break; + + default: //打印所有日志 + snprintf( log_str, LOG_STR_LEN, "SELECT * FROM better_log ORDER BY id DESC;" ); + break; + } + + if (sqlite3_exec( log->db, log_str, _log_show_print, 0, &zErrMsg ) != SQLITE_OK) + { + printh("SQL show error: %s\r\n", zErrMsg); + sqlite3_free(zErrMsg); + } + + LOG_DB_UNLOCK; +} + +/* 封装数据并写fifo */ +int32_t _log_msg_send(uint32_t type, void *data) +{ + _log_msg_t log_msg; + + /* 封装消息. */ + log_msg.type = type; + log_msg.data = data; + + /* 发送消息 */ + if( fifo_write( log_fifo.log_fifo_id, (void*)(&log_msg), sizeof(_log_msg_t)) != sizeof(_log_msg_t) ) + { + DBG( DBG_M_FIFO, "LOG write ERROR!\r\n" ); + return E_ERROR; + } + + return E_NONE; +} + +/* 经由fifo输出日志 */ +void _log_fifo_out( LOG_MODULE_E module, LOG_LVL_E priority, const char *va_str ) +{ + memset( log_out_struct, 0, sizeof(_log_out_t) ); + _log_out_t *log_data = log_out_struct; + log_data->module = module; + log_data->lvl = priority; + memcpy( log_data->log_out_str, va_str, LOG_STR_LEN ); + + _log_msg_send( LOG_OUT, log_data ); +} + +/* Interface functions -------------------------------------------------------*/ +/* log打印输出函数. */ +#define BLOG_FUNC(FUNCNAME,PRIORITY) \ +void FUNCNAME(LOG_MODULE_E module, const char *format, ...) \ +{ \ + log_sys_t *log = log_sys; \ + if (NULL == log) return; \ + LOG_LOCK; \ + memset( log_fifo_va_str, 0, LOG_STR_LEN ); \ + va_list args; \ + va_start(args, format); \ + vsnprintf(log_fifo_va_str, LOG_STR_LEN, format, args); \ + va_end(args); \ + if ((1 << PRIORITY) & log->enable_lvl[LOG_MODE_STDOUT]) \ + _log_out_std(module, PRIORITY, log_fifo_va_str); \ + if (((1 << PRIORITY) & log->enable_lvl[LOG_MODE_FILE]) && log->db){ \ + if (log_fifo.log_fifo_id) \ + _log_out_db(module, PRIORITY, log_fifo_va_str); \ + else \ + _log_fifo_out(module, PRIORITY, log_fifo_va_str);} \ + LOG_UNLOCK; \ +} + +BLOG_FUNC(log_err, LOG_LVL_ERR) +BLOG_FUNC(log_warn, LOG_LVL_WARN) +BLOG_FUNC(log_info, LOG_LVL_INFO) +BLOG_FUNC(log_notice, LOG_LVL_NOTIF) +BLOG_FUNC(log_debug, LOG_LVL_DBG) +#undef BLOG_FUNC + +/* description: log输出. + param: module -- 指定模块 + priority -- 指定优先级 + return: */ +void log_out(LOG_MODULE_E module, LOG_LVL_E priority, const char *format, ...) +{ + log_sys_t *log = log_sys; + if (NULL == log) return; + + LOG_OUT_LOCK; + + memset( log_out_va_str, 0, LOG_STR_LEN ); + va_list args; + va_start(args, format); + vsnprintf(log_out_va_str, LOG_STR_LEN, format, args); + va_end (args); + + /* 串口log. */ + if ((1 << priority) & log->enable_lvl[LOG_MODE_STDOUT]) + _log_out_std(module, priority, log_out_va_str); + + /* 判断是否是文件输出 */ + if (((1 << priority) & log->enable_lvl[LOG_MODE_FILE]) && log->db) + _log_out_db(module, priority, log_out_va_str); + + LOG_OUT_UNLOCK; +} + +/* 打印日志函数 */ +void log_show( int32_t show_cnt, LOG_LVL_E priority, const char *key_word ) +{ + memset( log_show_struct, 0, sizeof(_log_show_t) ); + _log_show_t *log_data = log_show_struct; + + if (priority != LOG_LVL_MAX) // 按等级打印日志 + { + log_data->type = LOG_SHOW_LVL; + log_data->param = priority; + } + else if (key_word != NULL) // 高级打印功能 + { + log_data->type = LOG_SHOW_KEYWORD; + log_data->param = show_cnt; + memcpy( log_data->log_show_str, key_word, LOG_STR_LEN ); + } + else if (show_cnt > 0) // 按数量打印日志 + { + log_data->type = LOG_SHOW_CNT; + log_data->param = show_cnt; + } + else + { + log_data->type = LOG_SHOW_MAX; + } + + _log_msg_send( LOG_SHOW, log_data ); +} + +/* description: log系统初始化. + param: + return: (0)完成,(-1)失败 */ +int32_t log_open() +{ + log_sys_t *log = NULL; + int32_t i = 0; + int32_t rv = 0; + char *sql = NULL; + char *zErrMsg = NULL; + + /* 申请内存. */ + log_str = XMALLOC(MTYPE_LOG, LOG_STR_LEN); + log_out_va_str = XMALLOC(MTYPE_LOG, LOG_STR_LEN); + log_fifo_va_str = XMALLOC(MTYPE_LOG, LOG_STR_LEN); + + log_out_struct = XMALLOC(MTYPE_LOG, sizeof(_log_out_t)); + log_show_struct = XMALLOC(MTYPE_LOG, sizeof(_log_show_t)); + log_sys = XMALLOC(MTYPE_LOG, sizeof(log_sys_t)); + + if (NULL == log_sys) + { + return E_MEM; + } + + log = log_sys; + + pthread_mutex_init(&log->log_mutex, NULL); + pthread_mutex_init(&log->log_out_mutex, NULL); + pthread_mutex_init(&log->log_db_mutex, NULL); + + /* 打开 log 数据库. */ + log->filename = XSTRDUP(MTYPE_LOG, LOG_FILE); + rv = sqlite3_open(log->filename, &log->db); + if (rv) + { + log->db = NULL; + printf("Can't open database: %s\r\n", sqlite3_errmsg(log->db)); + return E_SYS_CALL; + } + + /* 创建表. */ + sql = "CREATE TABLE IF NOT EXISTS better_log(" \ + "ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," \ + "LEVEL INT8 NOT NULL," \ + "LOG TEXT);"; + if (sqlite3_exec(log->db, sql, 0, 0, &zErrMsg) != SQLITE_OK) + { + printf("SQL create error: %s\r\n", zErrMsg); + sqlite3_free(zErrMsg); + } + + /* 设置默认log级别输出方式 */ + for (i = 0; i < LOG_MODE_MAX; i++) + { + log->enable_lvl[i] = 0; + } + log->enable_lvl[LOG_MODE_FILE] = (1 << LOG_LVL_ERR) | (1 << LOG_LVL_WARN) | (1 << LOG_LVL_NOTIF); + log->enable_lvl[LOG_MODE_STDOUT] = (1 << LOG_LVL_ERR) | (1 << LOG_LVL_WARN) | (1 << LOG_LVL_NOTIF); + log->enable_lvl[LOG_MODE_MONITOR] = (1 << LOG_LVL_ERR) | (1 << LOG_LVL_WARN) | (1 << LOG_LVL_NOTIF); + log->default_lvl = 0; + + return E_NONE; +} + +/* description: 配置对应等级的log的输出方式. + param: mode -- 输出方式 + log_level -- 输出等级 + return: */ +void log_set_level(LOG_MODE_E mode, LOG_LVL_E log_level) +{ + log_sys->enable_lvl[mode] |= (1 << log_level); +} + +/* description: 取消对应等级的log的输出方式. + param: mode -- 输出方式 + log_level -- 输出等级 + return: */ +void log_unset_level(LOG_MODE_E mode, LOG_LVL_E log_level) +{ + log_sys->enable_lvl[mode] &= ~(1 << log_level); +} + +/* description: 根据传入的字符串返回相应的log优先级. + param: lvl_name -- 输出等级的字符串 + return: (LOG_LVL_E)输出等级 */ +int32_t log_level_get_by_name(const char *lvl_name) +{ + int32_t level = LOG_LVL_MAX; + + for (level = 0 ; log_priority[level].describe != NULL ; level++) + if (!strncmp(lvl_name, log_priority[level].describe, 2)) + return log_priority[level].lvl; + + return LOG_LVL_MAX; +} + +/* 打印堆栈使用情况: */ +/* description: 根据传入的字符串返回相应的log优先级. + param: priority -- log优先级 + return: */ +void log_backtrace(int32_t priority) +{ + void *array[BACKTRACE_SIZE] = {NULL}; + int32_t size = 0; + int32_t i = 0; + char **strings = NULL; + + size = backtrace(array, BACKTRACE_SIZE); + if ((size <= 0) || ((size_t)size > BACKTRACE_SIZE)) + { + log_err(LOG_DEFAULT, "Cannot get backtrace, returned invalid # of frames %d " + "(valid range is between 1 and %d)", size, BACKTRACE_SIZE); + return; + } + + log_out(LOG_DEFAULT, priority, "Backtrace for %d stack frames:", size); + strings = backtrace_symbols(array, size); + if (!strings) + { + log_out(LOG_DEFAULT, priority, "Cannot get backtrace symbols (out of memory?)"); + for (i = 0; i < size; i++) + log_out(LOG_DEFAULT, priority, "[bt %d] %p", i, array[i]); + } + else + { + for (i = 0; i < size; i++) + log_out(LOG_DEFAULT, priority, "[bt %d] %s",i,strings[i]); + free(strings); + } +} + +/* 删除多余的 log 保留 5000 条. */ +void log_clean(void) +{ + log_sys_t *log = log_sys; + char *zErrMsg = NULL; + const char *sql = "delete from better_log where id<(select Max(id) from better_log)-5000;"; + + if (sqlite3_exec(log->db, sql, 0, 0, &zErrMsg) != SQLITE_OK) + { + printh("SQL delete error: %s\r\n", zErrMsg); + sqlite3_free(zErrMsg); + } +} + +/* 读取log fifo数据循环线程 */ +void *_log_handle( void *arg ) +{ + _log_msg_t *recv_msg = NULL; + + while(1) + { + if (fifo_read( log_fifo.log_fifo_id, (void **)&recv_msg ) != 0) + { + DBG( DBG_M_FIFO, "ERROR at fifo %d read!\r\n", log_fifo.log_fifo_id ); + continue; + } + + if (recv_msg->type == LOG_OUT) //输出日志,写数据库 + { + _log_out( (_log_out_t*) recv_msg->data ); + } + else if (recv_msg->type == LOG_SHOW) //打印日志,读数据库 + { + _log_show( (_log_show_t*) recv_msg->data ); + } + + fifo_push( log_fifo.log_fifo_id ); + } + return NULL; +} + +/* log线程初始化函数 */ +int32_t _log_init_common( void ) +{ + struct sched_param param; + pthread_attr_t attr; + pthread_t pid; + int rv = 0; + + /* 初始化log fifo */ + log_fifo.log_fifo_id = fifo_create( LOG_DB_FIFO, 32 ); + if (log_fifo.log_fifo_id < 0) + { + log_out( LOG_DEFAULT, LOG_LVL_ERR, "Open fifo " LOG_DB_FIFO " error." ); + return E_ERROR; + } + pthread_mutex_init( &log_fifo.mutex, NULL ); + + /* 配置线程RR调度,优先级50 */ + pthread_attr_init( &attr ); + param.sched_priority = 25; + pthread_attr_setschedpolicy( &attr, SCHED_RR ); + pthread_attr_setschedparam( &attr, ¶m ); + pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ); + rv = pthread_create( &pid, &attr, _log_handle, NULL ); + if (rv != 0) + { + log_out( LOG_DEFAULT, LOG_LVL_ERR, "PD can't create log db pthread %d.", rv ); + return E_SYS_CALL; + } + else + { + thread_m_add( "LOG_DB_THREAD", pid ); + } + pthread_attr_destroy( &attr ); + + return E_NONE; +} + +int32_t log_handle_init( void ) +{ + LD_E_RETURN( DBG_M_DBG, _log_init_common() ); + return E_NONE; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/cJSON.c b/app/lib/m_management/cJSON.c new file mode 100644 index 0000000..524ba46 --- /dev/null +++ b/app/lib/m_management/cJSON.c @@ -0,0 +1,3119 @@ +/* + 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 +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#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 (double) 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 != 15) + #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; + } + + 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 if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } + 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; + } + } + + 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); + if (replacement->string == NULL) + { + return false; + } + + 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; + } + + if (a && a->child) { + a->child->prev = 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; + } + + if (a && a->child) { + a->child->prev = 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; + } + + if (a && a->child) { + a->child->prev = 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; + } + + if (a && a->child) { + a->child->prev = 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; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + 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))) + { + 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/app/lib/m_management/cli.old b/app/lib/m_management/cli.old new file mode 100644 index 0000000..4799fb5 --- /dev/null +++ b/app/lib/m_management/cli.old @@ -0,0 +1,688 @@ +/****************************************************************************** + * file lib/management/cli.c + * author YuLiang + * version 1.0.0 + * date 10-Sep-2021 + * brief This file provides all the cli related operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +/* Standard includes. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +/* 外部程序库头文件. */ +#include +#include + +/* User includes. */ +#include "cli.h" +#include "list.h" + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +static int32_t _cli_help(const char *cmd_str); +static int32_t _cli_version(const char *cmd_str); +static int32_t _cli_exit(const char *cmd_str); + +/* The definition of the "help" command. This command is always at the front + of the list of registered commands. */ +static const cli_command_t _cli_cmd_help = +{ + "help", + "List all command.", + _cli_help, + 0 +}; + +static const cli_command_t _cli_cmd_version = +{ + "version", + "Show device version information.", + _cli_version, + 0 +}; + +static const cli_command_t _cli_cmd_exit = +{ + "exit", + "Returns the upper-level node.", + _cli_exit, + 0 +}; + +/* The definition of the list of commands. Commands that are registered are + added to this list. */ +static cli_node_t _cli_node[CLI_NODE_COUNT] = +{ + {"common", 0xffff, CLI_NODE_COMMON, {NULL, NULL}}, + {"Username: ", 0, CLI_NODE_PASSWORD, {NULL, NULL}}, + {"config", 0xffff, CLI_NODE_PASSWORD, {NULL, NULL}}, +}; + +/* 保存readline命令行历史的文件. */ +static const char _cli_history_file[128] = "/home/embed/.command_line_history"; +/* 保存readline命令行内容的buf. */ +static char *_cli_cmd_inpurt = NULL; +/* 当前cli所在的节点. */ +static CLI_NODE_E _cli_now = CLI_NODE_PASSWORD; +/* 命令行用户链表. */ +static struct list_head _cli_user_list; +/* cli线程pid. */ +static pthread_t _cli_pid; + +/* Private function prototypes -----------------------------------------------*/ +extern char* version_get(); +extern char* version_date_get(); + +static int8_t _cli_param_num_get(const char *cmd_str); +static cli_list_item_t *_cli_cmd_search(const char *cmd_str, CLI_NODE_E node); + +/* Internal functions --------------------------------------------------------*/ +/* 将node节点下的命令复制到cmd_list_new链表中并排序. */ +static int32_t _cli_cmd_copy_for_node(struct list_head *cmd_list_new, CLI_NODE_E node) +{ + struct list_head *cmd_list = NULL; + cli_list_item_t *cmd_item = NULL; + cli_list_item_t *cmd_item_new = NULL; + cli_list_item_t *cmd_item_temp = NULL; + cli_list_item_t *cmd_item_next = NULL; + int32_t rv = E_NONE; + + cmd_list = &_cli_node[node].list; + list_for_each_entry(cmd_item, cmd_list, list) + { + /* 复制命令 */ + cmd_item_new = XMALLOC(MTYPE_CLI, sizeof(cli_list_item_t)); + if (NULL == cmd_item_new) + { + goto CMD_COPY_FOR_NODE_ERROR; + } + memcpy(cmd_item_new, cmd_item, sizeof(cli_list_item_t)); + + /* 命令排序 */ + list_for_each_entry_reverse(cmd_item_temp, cmd_list_new, list) + { + if (strcmp(cmd_item_new->cmd->cmd_str, cmd_item_temp->cmd->cmd_str) > 0) + { + list_add(&cmd_item_new->list, &cmd_item_temp->list); + break; + } + } + + /* 最小的命令放在链表头 */ + if (&cmd_item_temp->list == cmd_list_new) + { + list_add(&cmd_item_new->list, &cmd_item_temp->list); + } + } + + return rv; + + /* 错误回滚已经申请的内存 */ +CMD_COPY_FOR_NODE_ERROR: + list_for_each_entry_safe(cmd_item_temp, cmd_item_next, cmd_list_new, list) + { + list_del(&cmd_item_temp->list); + XFREE(MTYPE_CLI, cmd_item_temp); + } + return rv; + +} + +/* The callback function tyhat is executed when "help" is entered. This is the + only default command that is always present. */ +static int32_t _cli_help(const char *cmd_str) +{ + struct list_head cmd_list_new; + cli_list_item_t *cmd_item_temp = NULL; + cli_list_item_t *cmd_item_next = NULL; + + INIT_LIST_HEAD(&cmd_list_new); + + /* 复制命令 */ + LD_E_RETURN(DBG_M_CLI, _cli_cmd_copy_for_node(&cmd_list_new, _cli_now)); + LD_E_RETURN(DBG_M_CLI, _cli_cmd_copy_for_node(&cmd_list_new, CLI_NODE_COMMON)); + + /* 打印信息 */ + list_for_each_entry(cmd_item_temp, &cmd_list_new, list) + { + printf("%-32s", cmd_item_temp->cmd->cmd_str); + printf("%s\r\n", cmd_item_temp->cmd->help_str); + } + printf("\n"); + + /* 释放内存 */ + list_for_each_entry_safe(cmd_item_temp, cmd_item_next, &cmd_list_new, list) + { + list_del(&cmd_item_temp->list); + XFREE(MTYPE_CLI, cmd_item_temp); + } + + return E_NONE; +} + +static int32_t _cli_version(const char *cmd_str) +{ + printf("Welcome To Power IoT Application, By LandPower Embed Application Team.\r\n"); + printf("Copyright(c) 2021 LandPower. All rights reserved.\r\n"); + printf("Software version %s, compile time is %s.\r\n\n", version_get(), version_date_get()); + + return E_NONE; +} + +static int32_t _cli_exit(const char *cmd_str) +{ + cli_quit_node(); + + return E_NONE; +} + +/* Return the number of parameters that follow the command name. */ +static int8_t _cli_param_num_get(const char *cmd_str) +{ + int8_t num = 0; + bool is_end = FALSE; + + /* Count the number of space delimited words in pcCommandString. */ + while(*cmd_str != 0) + { + if (' ' == (*cmd_str)) + { + if (is_end != TRUE) + { + num++; + is_end = TRUE; + } + } + else + { + is_end = FALSE; + } + + cmd_str++; + } + + /* If the command string ended with spaces, then there will have been too + many parameters counted. */ + if( is_end == TRUE ) + { + num--; + } + + /* The value returned is one less than the number of space delimited words, + as the first word should be the command itself. */ + return num; +} + +/* 命令行查找 */ +static cli_list_item_t *_cli_cmd_search(const char *cmd_str, CLI_NODE_E node) +{ + struct list_head *cmd_list = &_cli_node[node].list; + cli_list_item_t *cmd_item = NULL; + const char *cmd_str_register = NULL; + size_t cmd_str_len = 0; + + /* Search for the command string in the list of registered commands. */ + list_for_each_entry(cmd_item, cmd_list, list) + { + cmd_str_register = cmd_item->cmd->cmd_str; + cmd_str_len = strlen(cmd_str_register); + + /* To ensure the string lengths match exactly, so as not to pick up + a sub-string of a longer command, check the byte after the expected + end of the string is either the end of the string or a space before + a parameter. */ + if ((cmd_str[cmd_str_len] != ' ') && (cmd_str[cmd_str_len] != 0x00)) + { + continue; + } + + if (0 == strncmp(cmd_str, cmd_str_register, cmd_str_len)) + { + return cmd_item; + } + } + + return NULL; +} + +/* 获取命令行前缀 */ +char *_cli_prefix_get(char *host) +{ + static char buf[100] = {0}; + + if (0xffff == _cli_node[_cli_now].index) + { + snprintf(buf, sizeof(buf), "%s(%s)> ", host, _cli_node[_cli_now].name); + } + else + { + snprintf(buf, sizeof(buf), "%s(%s-%d)> ", host, _cli_node[_cli_now].name, _cli_node[_cli_now].index); + } + + return buf; +} + +/* readline初始化 */ +void _cli_readline_init(void) +{ + /* 修改自己的按键操作. */ + //rl_bind_key('?', (rl_command_func_t *)vtysh_rl_question); + //rl_bind_key('\t', (rl_command_func_t *)vtysh_rl_completion); + /* do not append space after completion. It will be appended + * in new_completion() function explicitly. */ + rl_completion_append_character = '\0'; + + read_history(_cli_history_file); +} + +/* 密码处理函数 */ +static int32_t _cli_process_password(const char * const cmd_str) +{ + static cli_user_t user_login; + cli_user_t *user = NULL; + + /* 表示在输入username */ + if (0 == _cli_node[_cli_now].index) + { + /* 保存username,并进入输入password模式 */ + _cli_node[_cli_now].index = 1; + snprintf(_cli_node[_cli_now].name, CLI_NODE_MAME_LEN, "Password: "); + snprintf(user_login.username, CLI_USERNAME_LEN, "%s", cmd_str); + + return E_NONE; + } + else + { + _cli_node[_cli_now].index = 0; + snprintf(_cli_node[_cli_now].name, CLI_NODE_MAME_LEN, "Username: "); + + /* 保存password,比较输入正确性 */ + snprintf(user_login.password, CLI_USERNAME_LEN, "%s", cmd_str); + list_for_each_entry(user, &_cli_user_list, list) + { + if (0 == strcmp(user_login.username, user->username) + && 0 == strcmp(user_login.password, user->password)) + { + /* 找到匹配 */ + printf("\r\n\n"); + cli_entry_node(CLI_NODE_CONFIG, 0xffff); + return E_NONE; + } + } + } + + return E_NOT_FOUND; +} + +/* 添加一个新用户 */ +int32_t _cli_user_add(const cli_user_t* const user) +{ + cli_user_t *user_new = NULL; + + user_new = XMALLOC(MTYPE_CLI, sizeof(cli_user_t)); + if (NULL == user_new) + { + return E_MEM; + } + memcpy(user_new, user, sizeof(cli_user_t)); + + list_add(&user_new->list, &_cli_user_list); + + return E_NONE; +} + +void *_cli_handle(void *arg) +{ + char *cmd = NULL; + + /* 主循环 */ + for(;;) + { + /* 输入命令. */ + cmd = cli_shell_gets(); + if (NULL == cmd) + { + continue; + } + + /* 执行命令. */ + cli_process_command(cmd); + } +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 命令行注册函数. + param: cmd -- 要注册的命令行结构体 + node -- 要注册到的节点 + return: E_NONE -- OK + 其他 -- ERROR */ +int32_t cli_register_command(const cli_command_t* const cmd, CLI_NODE_E node) +{ + struct list_head *cmd_list = &_cli_node[node].list; + cli_list_item_t *cmd_item = NULL; + + /* Check the parameter is not NULL. */ + if (NULL == cmd) + { + return E_NULL; + } + + /* Create a new list item that will reference the command being registered. */ + cmd_item = XMALLOC(MTYPE_CLI, sizeof(cli_list_item_t)); + if (NULL == cmd_item) + { + return E_MEM; + } + + /* Reference the command being registered from the newly created list item. */ + cmd_item->cmd = cmd; + + /* The new list item will get added to the end of the list. */ + list_add(&cmd_item->list, cmd_list); + + return E_NONE; +} + +/* description: 命令行注册函数. + param: cmd_str -- 要执行的命令行 + return: E_NONE -- OK + 其他 -- ERROR */ +int32_t cli_process_command(const char * const cmd_str) +{ + static const cli_list_item_t *cmd_item = NULL; + int32_t rv = E_NONE; + + /* 输入密码的过程不同于执行命令的过程 */ + if (CLI_NODE_PASSWORD == _cli_now) + { + if (_cli_process_password(cmd_str) != E_NONE) + { + printf("Username or password is not match!!!\r\n\n"); + return E_NOT_FOUND; + } + return E_NONE; + } + + cmd_item = _cli_cmd_search(cmd_str, (CLI_NODE_E)_cli_now); + if (NULL == cmd_item) + { + cmd_item = _cli_cmd_search(cmd_str, CLI_NODE_COMMON); + } + + /* The command has been found. Check it has the expected + number of parameters. If cExpectedNumberOfParameters is -1, + then there could be a variable number of parameters and no + check is made. */ + if (cmd_item != NULL) + { + if (cmd_item->cmd->param_num >= 0 + && _cli_param_num_get(cmd_str) > cmd_item->cmd->param_num ) + { + rv = E_BAD_PARAM; + } + } + + if ((cmd_item != NULL) && (rv != E_NONE)) + { + /* The command was found, but the number of parameters with the command + was incorrect. */ + printf("%s", cli_param_erro); + cmd_item = NULL; + } + else if(cmd_item != NULL) + { + /* Call the callback function that is registered to this command. */ + rv = cmd_item->cmd->function(cmd_str); + if (rv != E_NONE) + { + printf("Command(%s) return %d.\r\n\n", cmd_item->cmd->cmd_str, rv); + } + } + else + { + /* pxCommand was NULL, the command was not found. */ + printf("Command not recognised.\r\n\n"); + rv = E_NOT_FOUND; + } + + return rv; +} + +/* description: 进入命令行节点. + param: cmd_str -- 命令字符串 + param_index -- 需要获取的参数索引 + param_len -- 获取的参数长度 + return: char* -- 获取的参数字符串 */ +const char *cli_parameter_get(const char *cmd_str, int32_t param_index, OUT int32_t *param_len) +{ + int32_t index = 0; + const char *param_str = NULL; + + *param_len = 0; + + while(index < param_index) + { + /* Index the character pointer past the current word. If this is the start + of the command string then the first word is the command itself. */ + while(((*cmd_str) != 0x00 ) && ((*cmd_str) != ' ')) + { + cmd_str++; + } + + /* Find the start of the next string. */ + while(((*cmd_str) != 0x00) && (' ' == (*cmd_str))) + { + cmd_str++; + } + + /* Was a string found? */ + if (*cmd_str != 0x00) + { + /* Is this the start of the required parameter? */ + index++; + + if (index == param_index) + { + /* How long is the parameter? */ + param_str = cmd_str; + while(((*cmd_str) != 0x00 ) && ((*cmd_str) != ' ')) + { + (*param_len)++; + cmd_str++; + } + + if (*param_len == 0 || *param_len >= CLI_PARAM_LEN) + { + param_str = NULL; + } + + break; + } + } + else + { + break; + } + } + + return param_str; +} + +/* description: 进入命令行节点. + param: node -- 节点号 + index -- 节点内部索引 + return: E_NONE -- OK + 其他 -- ERROR */ +int32_t cli_entry_node(CLI_NODE_E node, int16_t index) +{ + if (node >= CLI_NODE_COUNT) + { + return E_BAD_PARAM; + } + + _cli_now = node; + _cli_node[_cli_now].index = index; + + return E_NONE; +} + +/* description: 返回上一级节点. + param: + return: */ +void cli_quit_node(void) +{ + _cli_now = _cli_node[_cli_now].upper_node; +} + +/* description: 命令行读取. + param: + return: char* -- 读取的一行命令 */ +char *cli_shell_gets(void) +{ + HIST_ENTRY *last = NULL; + struct termios new_setting,init_setting; + + /* readline要求自己释放其返回的buf. */ + if (_cli_cmd_inpurt) + { + free(_cli_cmd_inpurt); + _cli_cmd_inpurt = NULL; + } + + /* 获取一行命令. */ + if (CLI_NODE_PASSWORD == _cli_now) + { + /* 如果是输入密码则关闭回显. */ + if (1 == _cli_node[_cli_now].index) + { + tcgetattr(0, &init_setting); + new_setting = init_setting; + new_setting.c_lflag &= ~ECHO; + tcsetattr(0,TCSANOW,&new_setting); + } + + _cli_cmd_inpurt = readline(_cli_node[_cli_now].name); + + if (1 == _cli_node[_cli_now].index) + { + tcsetattr(0,TCSANOW,&init_setting); + } + } + else + { + _cli_cmd_inpurt = readline(_cli_prefix_get("Power_IoT")); + /* 如果命令有效记录历史. */ + if (_cli_cmd_inpurt && *_cli_cmd_inpurt) + { + using_history(); + last = previous_history(); + if (!last || strcmp (last->line, _cli_cmd_inpurt) != 0) + { + add_history(_cli_cmd_inpurt); + append_history(1, _cli_history_file); + } + } + } + + //if (strlen(_cli_cmd_inpurt) > 0) + //{ + // log_warn(LOG_CLI, "\"%s\" is input.", _cli_cmd_inpurt); + //} + + return _cli_cmd_inpurt; +} + +/* description: 命令行初始函数. + param: + return: */ +int32_t cli_init(void) +{ + struct sched_param param; + pthread_attr_t attr; + cli_user_t user_default; + int32_t i = 0; + int32_t rv = 0; + + /* 初始化readline */ + _cli_readline_init(); + + /* 初始化命令节点数据链表 */ + for(i = 0; i < CLI_NODE_COUNT; i++) + { + INIT_LIST_HEAD(&_cli_node[i].list); + } + + /* 注册公有命令 */ + cli_register_command(&_cli_cmd_help, CLI_NODE_COMMON); + cli_register_command(&_cli_cmd_version, CLI_NODE_COMMON); + cli_register_command(&_cli_cmd_exit, CLI_NODE_COMMON); + + /* 初始化用户列表 */ + INIT_LIST_HEAD(&_cli_user_list); + + /* 添加默认用户 */ + memset(&user_default, 0, sizeof(cli_user_t)); + snprintf(user_default.username, CLI_USERNAME_LEN, CLI_USERNAME_DEFAULT); + snprintf(user_default.password, CLI_USERNAME_LEN, CLI_PASSWORD_DEFAULT); + _cli_user_add(&user_default); + + /* 配置线程RR调度,优先级50 */ + pthread_attr_init(&attr); + param.sched_priority = 10; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + + rv = pthread_create(&_cli_pid, &attr, _cli_handle, NULL); + if (rv != 0) + { + DBG(DBG_M_CLI, "Can't create pthread %d\r\n", rv); + } + else + { + thread_m_add("CLI", _cli_pid); + } + pthread_attr_destroy(&attr); + + return E_NONE; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/cmd.c b/app/lib/m_management/cmd.c new file mode 100755 index 0000000..4add722 --- /dev/null +++ b/app/lib/m_management/cmd.c @@ -0,0 +1,3267 @@ +/****************************************************************************** + * file lib/management/cmd.c + * author YuLiang + * version 1.0.0 + * date 09-Oct-2021 + * brief This file provides all the command operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +/* 外部程序库头文件. */ +#include +#include + +/* 用户代码头文件. */ +#include "vty.h" +#include "cmd.h" +#include "sockunion.h" +#include "pd_cpld.h" +#include "list.h" + +/* Private define ------------------------------------------------------------*/ +#define VTYSH_CONFIG_R_BUF 512 + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +/* 主机信息结构. */ +host_t host; +device_info_t device_info; +int8_t is_system_init; + +/* 用于补齐回车,比如使用'?'或者'TAB'. */ +static desc_t cmd_desc_enter; +static char *cmd_enter = NULL; + +/* 多线程同时操作配置文件的互斥锁. */ +static pthread_mutex_t m_config_mutex; + +/* 命令行所有节点列表. */ +array_t *cmd_nodes = NULL; + +char *server_path = "server.socket"; +char *client_path = "client.socket"; + +vtycmd_t *vtycmd; +struct sockaddr_un cliun, serun; +int socklen; +#define MAXLINE 504 +extern cmd_node_t pd_port_node; +static cmd_node_t common_node = +{ + COMMON_NODE, + USERNAME_NODE, + 1, + "%s(common)# ", +}; + +static cmd_node_t username_node = +{ + USERNAME_NODE, + USERNAME_NODE, + 0, + "Username: ", +}; + +static cmd_node_t password_node = +{ + PASSWORD_NODE, + USERNAME_NODE, + 0, + "Password: ", +}; + +static cmd_node_t enable_node = +{ + ENABLE_NODE, + USERNAME_NODE, + 1, + "%s# ", +}; + +static cmd_node_t config_node = +{ + CONFIG_NODE, + ENABLE_NODE, + 1, + "%s(config)# ", +}; + +/* shell vty结构体. */ +vty_t *vtysh; + +/* 保存readline命令行内容的buf. */ +static char *line_read; + +/* 保存readline命令行历史的文件. */ +const char history_file[] = "./command_line_history"; + +/* cli线程pid. */ +static pthread_t _cmd_pid; + +extern int32_t vtysh_config_save_bak(int idx); + +/* 根据传入的字符串,返回相应log模块. */ +uint32_t _mode_match(const char *s) +{ + if (0 == strncmp(s, "f", 1)) + return LOG_MODE_FILE; + else if(0 == strncmp(s, "s", 1)) + return LOG_MODE_STDOUT; + else if(0 == strncmp(s, "m", 1)) + return LOG_MODE_MONITOR; + + return LOG_MODE_MAX; +} + +void vtycmd_send() +{ + if (vtysh->buf == NULL) + return; + vtycmd->buf_write.node = vtysh->node; + memset(&vtycmd->buf_write, 0, sizeof(vtycmd->buf_write)); + strcpy(vtycmd->buf_write.cmd, vtysh->buf); + sendto(vtycmd->socket, (char*)(&vtycmd->buf_write), strlen(vtycmd->buf_write.cmd) + 8, 0, (struct sockaddr *)&serun, socklen); + return; +} + +/* 设置设备名称. */ +CMD(mac_set, + mac_set_cmd, + "mac eth0 WORD", + "Set mac\n" + "HH:HH:HH:HH:HH:HH\n") +{ + return CMD_SUCCESS; + + uint8_t mac[MAC_ADDR_LEN]; + + if (str_to_mac((char*)argv[0], mac) != E_NONE) + { + vty_out(vty, "Mac addr is not valid!%s", VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + + memcpy(device_info.mac, mac, MAC_ADDR_LEN); + vtysh_device_save(); + vtysh_eth0_save(); + + return CMD_SUCCESS; +} + +/* 设置设备名称. */ +CMD(id_set_cli, + id_set_cmd, + "id WORD", + "Set id\n" + "DDDD.DDDD\n") +{ + return CMD_SUCCESS; + + uint32_t id[2] = {0}; + + if (str_to_id((char*)argv[0], id) != E_NONE) + { + vty_out(vty, "ID is not valid!%s", VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + device_info.id_major = id[0]; + device_info.id_minor = id[1]; + vtysh_device_save(); + + return CMD_SUCCESS; +} + +/* 设置设备名称. */ +CMD(hostname_set, + hostname_set_cmd, + "hostname WORD", + "Set hostname\n" + "hostname\n") +{ + return CMD_SUCCESS; + snprintf(host.name, FILE_NAME_LEN, "%s", argv[0]); + + return CMD_SUCCESS; +} + +/* 基本命令: 进入enable模式. */ +CMD(config_enable, + config_enable_cmd, + "enable", + "Turn on privileged mode command\n") +{ + vty->node = ENABLE_NODE; + + return CMD_SUCCESS; +} + +/* 基本命令:进入shell模式. */ +CMD(bash_open, + bash_open_cmd, + "bash", + "Open a shell terminal\n") +{ + return CMD_SUCCESS; + if (system("bash") <= 0) + { + vty_out(vty, "Entry bash error!%s", VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + + return CMD_SUCCESS; +} + +/* 设置终端的显示模式. */ +CMD(terminal_monitor, + terminal_monitor_cmd, + "terminal monitor", + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 1; + return CMD_SUCCESS; +} + +/* 设置终端的显示模式. */ +CMD(no_terminal_monitor, + no_terminal_monitor_cmd, + "no terminal monitor", + NO_STR + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 0; + return CMD_SUCCESS; +} + +/* 基本命令:进入config模式. */ +CMD(config_terminal, + config_terminal_cmd, + "configure", + "Configuration from vty interface\n") +{ + return CMD_SUCCESS; + + if (vty_config_lock(vty)) + vty->node = CONFIG_NODE; + else + { + vty_out(vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* 基本命令:显示版本信息. */ +CMD(show_version, + show_version_cmd, + "show version", + SHOW_STR + "Displays zebra version\n") +{ + return CMD_SUCCESS; + vty_version_print(vty); + vty_out(vty, "%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* 基本命令:退出当前模式. */ +CMD(exit_node, + exit_node_cmd, + "exit", + "Exit current mode\n") +{ + return CMD_SUCCESS; + cmd_node_t *node = cmd_node_get(vty->node); + if (NULL == node) + return CMD_WARNING; + + vty->node = node->up_node; + + if (CONFIG_NODE == node->node) + vty_config_unlock(vty); + + return CMD_SUCCESS; +} +ALIAS(exit_node, + quit_node_cmd, + "quit", + "Exit current mode\n") + +CMD(logoff_cli, + logoff_cmd, + "logoff", + "Exit app\n") +{ + return CMD_SUCCESS; + exit(-1); + + return CMD_SUCCESS; +} + +CMD(reboot_node, + reboot_cmd, + "reboot", + "reset system\n") +{ + return CMD_SUCCESS; + reboot_system(DBG_M_CLI, BOOT_SYSTEM_RESET); + + return CMD_SUCCESS; +} + + +/* 配置log模块的输出级别. */ +CMD(config_log_level, + config_log_level_cmd, + "log (stdout|file|monitor) "LOG_LEVELS, + "Logging control\n" + "Set stdout logging level\n" + "Set file logging level\n" + "Set terminal line (monitor) logging level\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; + + LOG_MODE_E log_mode = _mode_match(argv[0]); + LOG_LVL_E level = log_level_get_by_name(argv[1]); + + if (LOG_LVL_MAX == level) + return CMD_ERR_NO_MATCH; + + if (log_mode >= LOG_MODE_MAX) + return CMD_ERR_NO_MATCH; + + log_set_level(log_mode, level); + + return CMD_SUCCESS; +} + +/* 配置log模块的输出级别. */ +CMD(no_config_log_level, + no_config_log_level_cmd, + "no log (stdout|file|monitor) "LOG_LEVELS, + NO_STR + "Logging control\n" + "Set stdout logging level\n" + "Set file logging level\n" + "Set terminal line (monitor) logging level\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; + + uint32_t log_mode = _mode_match(argv[0]); + int32_t level = log_level_get_by_name(argv[1]); + + if (LOG_LVL_MAX == level) + return CMD_ERR_NO_MATCH; + + if (log_mode >= LOG_MODE_MAX) + return CMD_ERR_NO_MATCH; + + log_unset_level(log_mode, level); + + return CMD_SUCCESS; +} + +/* 默认串口输出日志 */ +CMD(show_log_default, + show_log_default_cmd, + "show log", + SHOW_STR + LOG_STR) +{ + return CMD_SUCCESS; + + log_show( LOG_DEFAULT_SHOW_CNT, LOG_LVL_MAX, NULL ); + return CMD_SUCCESS; +} + +CMD(show_log_level, + show_log_level_cmd, + "show log "LOG_LEVELS, + SHOW_STR + LOG_STR + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; + + int32_t level = log_level_get_by_name( argv[0] ); + log_show( LOG_DEFAULT_SHOW_CNT, level, NULL ); + return CMD_SUCCESS; +} + +CMD(show_log_cnt, + show_log_cnt_cmd, + "show log <0-5000>", + SHOW_STR + LOG_STR + "Count of logs, will show all as it == 0, can enter the keyword of logs afterword\n") +{ + return CMD_SUCCESS; + + int32_t cnt = atoi( argv[0] ); + + log_show( cnt, LOG_LVL_MAX, NULL ); + return CMD_SUCCESS; +} + +CMD(show_log, + show_log_cmd, + "show log <0-5000> WORD", + SHOW_STR + LOG_STR + "Count of logs, will show all as it == 0, can enter the keyword of logs afterword\n" + "Keyword of logs\n") +{ + return CMD_SUCCESS; + + int32_t cnt = atoi( argv[0] ); + + log_show( cnt, LOG_LVL_MAX, argv[1] ); + return CMD_SUCCESS; +} + +CMD(host_ip_set, + host_ip_set_cmd, + "host ip A.B.C.D A.B.C.D", + "Host ip for produce\n" + "IPv4 type address\n" + "Ip address of host\n" + "Ip mask of host\n") +{ + return CMD_SUCCESS; + + vtysh_host_addr_set((char*)argv[0], (char*)argv[1]); + + return CMD_SUCCESS; +} + +CMD(default_route_set, + default_route_set_cmd, + "default route A.B.C.D", + "Default route set\n" + "Default route\n" + "Address of default route\n") +{ + return CMD_SUCCESS; + + vtysh_gateway_set((char*)argv[0]); + + return CMD_SUCCESS; +} + +CMD(factory_date_set, + factory_date_set_cmd, + "factory date WORD WORD", + "factory\n" + "factory date\n" + "date\n" + "time\n") +{ + return CMD_SUCCESS; + + uint32_t t = 0; + + if (time_str_to_long((char*)argv[0], (char*)argv[1], &t) != E_NONE) + { + vty_out(vty, "Factory date set error!%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + device_info.factory_date = t; + vtysh_device_save(); + + return CMD_SUCCESS; +} + +CMD(deployment_date_set, + deployment_date_set_cmd, + "deployment date WORD WORD", + "deployment\n" + "deployment date\n" + "date dddd-dd-dd\n" + "time dd:dd:dd\n") +{ + return CMD_SUCCESS; + + uint32_t t = 0; + + if (time_str_to_long((char*)argv[0], (char*)argv[1], &t) != E_NONE) + { + vty_out(vty, "Factory date set error!%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + device_info.deployment_date = t; + vtysh_device_save(); + + return CMD_SUCCESS; +} + +/* 把当前配置写入配置文件. */ +CMD(config_write_file, + config_write_file_cmd, + "write file", + "Write running configuration to memory, network, or terminal\n" + "Write to configuration file\n") +{ + return CMD_SUCCESS; + + if (vtysh_config_save() != E_NONE) + { + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* 在终端上显示当前配置信息. */ +CMD(config_write_terminal, + show_running_config_cmd, + "show running-config", + SHOW_STR + "running configuration\n") +{ + return CMD_SUCCESS; + + uint32_t i = 0; + cmd_node_t *node = NULL; + + vty_out(vty, "Current configuration:%s", VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + + for(i = 0; i < array_active(cmd_nodes); i++) + if ((node = array_get(cmd_nodes, i)) && node->func) + { + if ((*node->func)(vty)) + vty_out(vty, "!%s", VTY_NEWLINE); + } + + vty_out(vty, "end%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* 在终端上显示当前配置信息. */ +CMD(enable_test, + enable_test_cmd, + "test <0-3>", + ROUTE_STR + ROUTE_STR) +{ + return CMD_SUCCESS; + + uint16_t utc = 0; + uint16_t temp = 0; + + temp = strtol(argv[0], NULL, 10); + + //utc = 0; + //cpld_write(0xc, 1, &utc); + //sleep(1); + cpld_write(0xb, 1, &temp); + //utc = 3; + //cpld_write(0xc, 1, &utc); + //sleep(1); + + + return CMD_SUCCESS; +} + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +/* 用于自动补齐命令行关键字. */ +void _vtysh_append_word(array_t *cmd_line, char *word, int32_t status) +{ + uint32_t index = array_active(cmd_line) - 1; + uint32_t len = 0; + uint32_t start = 0; + + if (NULL == array_get(cmd_line, index)) + rl_insert_text(word); + else + { + len = strlen(array_get(cmd_line, index)); + start = rl_end - len; + + rl_begin_undo_group(); + rl_delete_text(start, rl_end); + rl_point = start; + rl_insert_text(word); + rl_end_undo_group(); + } + + if (CMD_COMPLETE_FULL_MATCH == status) + rl_insert_text(" "); +} + +/* config模式配置保存函数: vty -- 相应的终端 */ +int32_t _config_write_host(vty_t *vty) +{ + array_t *configs = config_node.configs; + cmd_save_config_f *func = NULL; + uint16_t i = 0; + + if (host.name) + vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + + /* 其他配置保存 */ + for(i = 0; i < array_active(configs); i++) + { + func = array_lookup(configs, i); + if (!func) + { + continue; + } + + if (func(vty)) + { + vty_out(vty, "!%s", VTY_NEWLINE); + } + } + + return E_NONE; +} + +/* 获取命令串的描述: + string -- 描述字符串(OUT) + + return: 命令行描述字符串,失败返回NULL */ +static char *_cmd_desc_str(const char **string) +{ + const char *cp = NULL; + const char *start = NULL; + char *str = NULL; + int32_t strlen = 0; + + cp = *string; + + if (NULL == cp) + { + return NULL; + } + + /* 跳过空格 */ + while (isspace ((int) *cp) && *cp != '\0') + { + cp++; + } + + if ('\0' == *cp) + { + return NULL; + } + + /* 获取字符串 */ + start = cp; + + while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') + { + cp++; + } + + strlen = cp - start; + str = XMALLOC(MTYPE_CLI ,strlen + 1); + memcpy(str, start, strlen); + *(str + strlen) = '\0'; + + /* 更新描述字符串的指针. */ + *string = cp; + + return str; +} + +/* 解析命令行字符串,将其每个单词拆分存入数组中: + string -- 命令行字符串 + descstr -- 命令行帮助字符串 + + return: 命令行字符串结构数组,失败返回NULL */ +array_t *_cmd_strs_create(const char *string, const char *descstr) +{ + uint32_t multiple = 0; + const char *sp = NULL; + char *word = NULL; + uint32_t len = 0; + const char *cp = NULL; + const char *dp = NULL; + array_t *strs = NULL; + array_t *descs = NULL; + desc_t *desc = NULL; + + cp = string; + dp = descstr; + + if (NULL == cp) + { + return NULL; + } + + strs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); + + while (1) + { + /* 跳过多余空格 */ + while (isspace((int)(*cp)) && *cp != '\0') + { + cp++; + } + + if ('(' == *cp) + { + multiple = 1; + cp++; + } + + if (')' == *cp) + { + multiple = 0; + cp++; + } + + if ('|' == *cp) + { + if (!multiple) { + log_err(LOG_CLI, "Command parse error!: %s", string); + exit(1); + } + cp++; + } + + while (isspace((int)(*cp)) && *cp != '\0') + { + cp++; + } + + if ('(' == *cp) + { + multiple = 1; + cp++; + } + + if ('\0' == *cp) + { + return strs; + } + + sp = cp; + + while (!(isspace((int) *cp) || '\r' == *cp || '\n' == *cp || ')' == *cp || '|' == *cp ) + && *cp != '\0') + { + cp++; + } + + /* 获取字符串 */ + len = cp - sp; + word = XMALLOC(MTYPE_CLI, len + 1); + memcpy(word, sp, len); + *(word + len) = '\0'; + + /* 创建命令行当个单词结构 */ + desc = XMALLOC(MTYPE_CLI, sizeof(desc_t)); + desc->cmd = word; + desc->str = _cmd_desc_str(&dp); + + if (multiple) + { + /* 多选的命令放在同一个数组下 */ + if (1 == multiple) + { + descs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); + array_append(strs, descs, MTYPE_CLI); + } + multiple++; + } + else + { + /* 单个命令放在一个独立的数组下 */ + descs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); + array_append(strs, descs, MTYPE_CLI); + } + array_append(descs, desc, MTYPE_CLI); + } +} + +/* 统计命令有效单词个数: + strs -- 命令行字符串数组 + + return: 命令行包含的字符串的个数 */ +static uint32_t _cmd_descs_size(array_t *strs) +{ + uint32_t i = 0; + uint32_t size = 0; + array_t *descs = NULL; + desc_t *desc = NULL; + + /* 遍历所有字符串 */ + for (i = 0; i < array_active(strs); i++) + { + if (NULL == (descs = array_get(strs, i))) + { + continue; + } + + /* 多选项直接++, 单选项如果不是NULL或者可选项直接++ */ + if (1 == (array_active(descs)) + && (desc = array_get (descs, 0)) != NULL) + { + if (NULL == desc->cmd || CMD_OPTION(desc->cmd)) + { + return size; + } + else + { + size++; + } + } + else + { + size++; + } + } + + return size; +} + +/* 命令行字符串比较函数, 用于cmd_sort_node(): + p -- 命令行节点1 + q -- 命令行节点2 + + return: 等于返回0,大于返回正数,小于返回负数 */ +static int _cmp_node (const void *p, const void *q) +{ + const cmd_element_t *a = *(cmd_element_t * const *)p; + const cmd_element_t *b = *(cmd_element_t * const *)q; + + return strcmp(a->string, b->string); +} + +/* 命令字符串排序: + p -- 命令字符串1 + q -- 命令字符串2 + + return: 等于返回0,大于返回正数,小于返回负数 */ +static int _cmp_desc (const void *p, const void *q) +{ + const desc_t *a = *(desc_t * const *)p; + const desc_t *b = *(desc_t * const *)q; + + return strcmp(a->cmd, b->cmd); +} + +/* 获取命令行节点下所有命令数组: + ntype -- 命令节点 + + return: 节点上所有命令行数组 */ +static array_t *_cmd_node_cmds_get(NODE_TYPE_E ntype) +{ + cmd_node_t *node = array_get(cmd_nodes, ntype); + return node->cmds; +} + +/* 检查字符串str是否符合range: + range -- 范围字符串 + str -- 需要检测字符串 + + return: 匹配返回1 */ +static int _cmd_range_match(const char *range, const char *str) +{ + char *p = NULL; + char buf[DECIMAL_STRLEN_MAX + 1] = {0}; + char *endptr = NULL; + unsigned long min = 0; + unsigned long max = 0; + unsigned long val = 0; + + if (NULL == str) + return 1; + + /* 将字符串转为十进制数. */ + val = strtoul(str, &endptr, 10); + if (*endptr != '\0') + return 0; + + /* 是否符合格式 */ + range++; + p = strchr(range, '-'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + min = strtoul(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + /* 是否符合格式 */ + range = p + 1; + p = strchr (range, '>'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + + strncpy (buf, range, p - range); + buf[p - range] = '\0'; + max = strtoul (buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + if (val < min || val > max) + return 0; + + return 1; +} + +/* 检查字符串是不是ipv6地址: + str -- ipv6字符串 + + return: 部分匹配, 完全匹配, 不匹配 */ +static MATCH_TYPE_E _cmd_ipv6_match(const char *str) +{ + uint32_t state = STATE_START; + uint32_t colons = 0; + uint32_t nums = 0; + uint32_t double_colon = 0; + const char *sp = NULL; + struct sockaddr_in6 sin6_dummy; + int ret; + + if (NULL == str) + return partly_match; + + if (strspn(str, IPV6_ADDR_STR) != strlen (str)) + return no_match; + + /* use inet_pton that has a better support, + * for example inet_pton can support the automatic addresses: + * ::1.2.3.4 + */ + ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); + if (1 == ret) + return exact_match; + + while(*str != '\0') + { + switch (state) + { + case STATE_START: + if (*str == ':') + { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } + else + { + sp = str; + state = STATE_ADDR; + } + + continue; + + case STATE_COLON: + colons++; + if (*(str + 1) == ':') + state = STATE_DOUBLE; + else + { + sp = str + 1; + state = STATE_ADDR; + } + break; + + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else + { + if (*(str + 1) != '\0') + colons++; + sp = str + 1; + state = STATE_ADDR; + } + + double_colon++; + nums++; + break; + + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '\0') + { + if (str - sp > 3) + return no_match; + + nums++; + state = STATE_COLON; + } + if (*(str + 1) == '.') + state = STATE_DOT; + break; + + case STATE_DOT: + state = STATE_ADDR; + break; + + default: + break; + } + + if (nums > 8) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + return exact_match; +} + +/* 检查字符串是不是带掩码的ipv6地址: + str -- ipv6字符串 + + return: 部分匹配, 完全匹配, 不匹配 */ +static MATCH_TYPE_E _cmd_ipv6_prefix_match(const char *str) +{ + uint32_t state = STATE_START; + int colons = 0; + uint32_t nums = 0; + uint32_t double_colon = 0; + uint32_t mask; + const char *sp = NULL; + char *endptr = NULL; + + if (NULL == str) + return partly_match; + + if (strspn(str, IPV6_PREFIX_STR) != strlen(str)) + return no_match; + + while (*str != '\0' && state != STATE_MASK) + { + switch (state) + { + case STATE_START: + if (':' == *str) + { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } + else + { + sp = str; + state = STATE_ADDR; + } + continue; + + case STATE_COLON: + colons++; + if (*(str + 1) == '/') + return no_match; + else if (*(str + 1) == ':') + state = STATE_DOUBLE; + else + { + sp = str + 1; + state = STATE_ADDR; + } + break; + + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else + { + if (*(str + 1) != '\0' && *(str + 1) != '/') + colons++; + sp = str + 1; + + if (*(str + 1) == '/') + state = STATE_SLASH; + else + state = STATE_ADDR; + } + + double_colon++; + nums += 1; + break; + + case STATE_ADDR: + if (*(str + 1) == ':' || + *(str + 1) == '.' || + *(str + 1) == '\0' || + *(str + 1) == '/') + { + if (str - sp > 3) + return no_match; + + for (; sp <= str; sp++) + if (*sp == '/') + return no_match; + + nums++; + + if (*(str + 1) == ':') + state = STATE_COLON; + else if (*(str + 1) == '.') + state = STATE_DOT; + else if (*(str + 1) == '/') + state = STATE_SLASH; + } + break; + + case STATE_DOT: + state = STATE_ADDR; + break; + + case STATE_SLASH: + if (*(str + 1) == '\0') + return partly_match; + + state = STATE_MASK; + break; + + default: + break; + } + + if (nums > 11) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + if (state < STATE_MASK) + return partly_match; + + mask = strtol (str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (mask < 0 || mask > 128) + return no_match; + + return exact_match; +} + +/* 检查字符串是不是ipv4地址: + str -- ipv4字符串 + + return: 部分匹配, 完全匹配, 不匹配 */ +static MATCH_TYPE_E _cmd_ipv4_match(const char *str) +{ + const char *sp; + uint32_t dots = 0; + uint32_t nums = 0; + char buf[4] = {0}; + + if (NULL == str) + return partly_match; + + while(1) + { + memset(buf, 0, sizeof (buf)); + sp = str; + while(*str != '\0') + { + if (*str == '.') + { + if (dots >= 3) + return no_match; + + if (*(str + 1) == '.') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy(buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + nums++; + + if (*str == '\0') + break; + + str++; + } + + if (nums < 4) + return partly_match; + + return exact_match; +} + +/* 检查字符串是不是带掩码的ipv4地址: + str -- ipv4字符串 + + return: 部分匹配, 完全匹配, 不匹配 */ +static MATCH_TYPE_E _cmd_ipv4_prefix_match(const char *str) +{ + const char *sp = NULL; + int dots = 0; + char buf[4] = {0}; + + if (NULL == str) + return partly_match; + + while(1) + { + memset(buf, 0, sizeof (buf)); + sp = str; + while (*str != '\0' && *str != '/') + { + if (*str == '.') + { + if (dots == 3) + return no_match; + + if (*(str + 1) == '.' || *(str + 1) == '/') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + + if (!isdigit((int)(*str))) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy (buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + if (3 == dots) + { + if ('/' == *str) + { + if (*(str + 1) == '\0') + return partly_match; + + str++; + break; + } + else if (*str == '\0') + return partly_match; + } + + if (*str == '\0') + return partly_match; + + str++; + } + + sp = str; + while(*str != '\0') + { + if (!isdigit((int)(*str))) + return no_match; + + str++; + } + + if (atoi(sp) > 32) + return no_match; + + return exact_match; +} + +/* cl_str与所有命令匹配,将没有匹配的cmd在cmds中置为NULL: + cl_str -- 实际输入的命令 + cmds -- 所有命令, + index -- cl_str在命令行中的位置 + + return: 返回匹配结果 */ +static MATCH_TYPE_E _cmd_filter_by_completion(char *cl_str,array_t *cmds,uint32_t index) +{ + uint32_t i = 0, j = 0; + int matched = 0; + const char *str = NULL; + cmd_element_t *cmd = NULL; + MATCH_TYPE_E match_type = no_match; + array_t *strs = NULL; + desc_t *desc = NULL; + + /* 如果cmds中的命令在index位置上不能配置cl_str,在cmds中将会被设置为NULL. */ + for (i = 0; i < array_active(cmds); i++) + { + /* 跳过空位. */ + if (NULL == (cmd = array_get(cmds, i))) + { + continue; + } + + /* 长度不够的命令直接排除. */ + if (index >= array_active(cmd->strs)) + { + array_get(cmds, i) = NULL; + continue; + } + + matched = 0; + + /* 如果strs是命令中的参数,有可能有多个可选项,所以要做循环. */ + strs = array_get(cmd->strs, index); + for (j = 0; j < array_active(strs); j++) + { + if (NULL == (desc = array_get(strs, j))) + { + continue; + } + + /* 比较是有优先级的,如果在index位置有多种类型的匹配, + 这里只会返回优先级最高的类型. */ + str = desc->cmd; + if (CMD_VARARG(str)) + { + if (match_type < vararg_match) + match_type = vararg_match; + matched++; + } + else if (CMD_RANGE(str)) + { + if (_cmd_range_match(str, cl_str)) + { + if (match_type < range_match) + { + match_type = range_match; + } + matched++; + } + } + else if (CMD_IPV6(str)) + { + if (_cmd_ipv6_match(cl_str)) + { + if (match_type < ipv6_match) + { + match_type = ipv6_match; + } + matched++; + } + } + else if (CMD_IPV6_PREFIX(str)) + { + if (_cmd_ipv6_prefix_match(cl_str)) + { + if (match_type < ipv6_prefix_match) + { + match_type = ipv6_prefix_match; + } + matched++; + } + } + else if (CMD_IPV4(str)) + { + if (_cmd_ipv4_match(cl_str)) + { + if (match_type < ipv4_match) + { + match_type = ipv4_match; + } + matched++; + } + } + else if (CMD_IPV4_PREFIX(str)) + { + if (_cmd_ipv4_prefix_match(cl_str)) + { + if (match_type < ipv4_prefix_match) + { + match_type = ipv4_prefix_match; + } + matched++; + } + } + else + { + /* Check is this point's argument optional ? */ + if (CMD_OPTION(str) || CMD_VARIABLE(str)) + { + if (match_type < extend_match) + { + match_type = extend_match; + } + matched++; + } + else if (0 == strncmp(cl_str, str, strlen(cl_str))) + { + if (0 == strcmp(cl_str, str)) + { + match_type = exact_match; + } + else + { + if (match_type < partly_match) + { + match_type = partly_match; + } + } + matched++; + } + } + } + + /* 将不匹配的命令排除. */ + if (!matched) + { + array_get(cmds, i) = NULL; + } + } + + return match_type; +} + +/* 根据配置类型type,进一步剔除cmds中不符合的cmd: + cl_str -- 命令行字符串 + cmds -- 所有可用命令数组 + index -- 命令行字符串索引 + type -- 上一次匹配类型 + + return: 有多个模糊匹配返回1, ipv4 or ipv6匹配不完整返回2, 成功返回0 */ +static int32_t _cmd_is_ambiguous(char *cl_str, array_t *cmds, uint32_t index, MATCH_TYPE_E type) +{ + uint32_t i = 0, j = 0; + int32_t match = 0; + const char *str = NULL; + cmd_element_t *cmd = NULL; + const char *matched = NULL; + array_t *strs = NULL; + desc_t *desc = NULL; + MATCH_TYPE_E ret = no_match; + + /* 遍历所有命令 */ + for (i = 0; i < array_active(cmds); i++) + { + if (NULL == (cmd = array_get(cmds, i))) + { + continue; + } + + match = 0; + /* 获取命令行第index字符串 */ + strs = array_get(cmd->strs, index); + + /* 遍历该字符串下所有可能的关键字 */ + for (j = 0; j < array_active(strs); j++) + { + if (NULL == (desc = array_get(strs, j))) + { + continue; + } + + ret = no_match; + str = desc->cmd; + + switch (type) + { + case exact_match: + if (!(CMD_OPTION(str) || CMD_VARIABLE(str)) + && 0 == strcmp(cl_str, str)) + { + match++; + } + break; + + case partly_match: + if (!(CMD_OPTION(str) || CMD_VARIABLE(str)) + && 0 == strncmp(cl_str, str, strlen(cl_str))) + { + if (matched && strcmp(matched, str) != 0) + { + return 1; /* There is ambiguous match. */ + } + else + { + matched = str; + } + match++; + } + break; + + case range_match: + if (_cmd_range_match(str, cl_str)) + { + if (matched && strcmp(matched, str) != 0) + { + return 1; + } + else + { + matched = str; + } + match++; + } + break; + + case ipv6_match: + if (CMD_IPV6(str)) + { + match++; + } + break; + + case ipv6_prefix_match: + if ((ret = _cmd_ipv6_prefix_match(cl_str)) != no_match) + { + if (ret == partly_match) + { + return 2; /* There is incomplete match. */ + } + match++; + } + break; + + case ipv4_match: + if (CMD_IPV4(str)) + { + match++; + } + break; + + case ipv4_prefix_match: + if ((ret = _cmd_ipv4_prefix_match(cl_str)) != no_match) + { + if (ret == partly_match) { + return 2; /* There is incomplete match. */ + } + match++; + } + break; + + case extend_match: + if (CMD_OPTION(str) || CMD_VARIABLE(str)) + { + match++; + } + break; + + case no_match: + default: + break; + } + } + + /* 将类型不匹配的命令排除. */ + if (!match) + { + array_get(cmds, i) = NULL; + } + } + + return 0; +} + +/* 获取命令节点下所有命令数组和公共命令节点上的所有命令数组合并: + ntype -- 命令节点 + + return: 返回复制的命令行数组 + node: 在非VIEW_NODE模式下总会复制公共节点上的命令行 */ +static array_t *_cmd_cmds_create(NODE_TYPE_E ntype) +{ + array_t *cmds = array_copy(_cmd_node_cmds_get(ntype), MTYPE_CLI); + + if (ntype != PASSWORD_NODE && ntype != USERNAME_NODE) + { + array_merge(cmds, _cmd_node_cmds_get(COMMON_NODE), MTYPE_CLI); + } + + return cmds; +} + +/* 从vty执行命令cmd_line. */ +static int32_t _cmd_execute_command_real(array_t *cmd_line, vty_t *vty, cmd_element_t **cmd) +{ + uint32_t i = 0; + uint32_t index = 0; + array_t *cmds = NULL; + cmd_element_t *cmd_element = NULL; + cmd_element_t *matched_element = NULL; + uint32_t matched_count = 0; + uint32_t incomplete_count = 0; + int32_t argc = 0; + const char *argv[CMD_ARGC_MAX] = {NULL}; + MATCH_TYPE_E match = no_match; + int32_t varflag = 0; + char *cl_str = NULL; + + /* 将该节点下的所有命令复制一份. */ + cmds = _cmd_cmds_create(vty->node); + + /* 查找符合的cmd. */ + for(index = 0; index < array_active(cmd_line); index++) + { + if (NULL == (cl_str = array_get(cmd_line, index))) + { + continue; + } + + int ret = 0; + + /* 匹配cl_str和cmds中index位置字符串并返回配置类型. */ + match = _cmd_filter_by_completion(cl_str, cmds, index); + if (vararg_match == match) + { + break; + } + + /* 根据配置类型type,进一步剔除cmds中不符合的cmd. */ + ret = _cmd_is_ambiguous(cl_str, cmds, index, match); + if (1 == ret) + { + array_free(cmds, MTYPE_CLI); + return CMD_ERR_AMBIGUOUS; + } + else if (2 == ret) + { + array_free(cmds, MTYPE_CLI); + return CMD_ERR_NO_MATCH; + } + } + + /* 检查是否只有唯一匹配并取出该命令成员. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + + for (i = 0; i < array_active(cmds); i++) + { + if (NULL == (cmd_element = array_get(cmds, i))) + { + continue; + } + + if (vararg_match == match || index >= cmd_element->str_size) + { + matched_element = cmd_element; + matched_count++; + } + else + { + incomplete_count++; + } + } + + array_free(cmds, MTYPE_CLI); + + if (0 == matched_count) + { + if (incomplete_count) + { + return CMD_ERR_INCOMPLETE; + } + else + { + return CMD_ERR_NO_MATCH; + } + } + + if (matched_count > 1) + { + return CMD_ERR_AMBIGUOUS; + } + + /* 将命令的参数解析出来. */ + varflag = 0; + argc = 0; + + for (i = 0; i < array_active(cmd_line); i++) + { + if (varflag) + { + argv[argc++] = array_get(cmd_line, i); + } + else + { + array_t *strs = array_get(matched_element->strs, i); + + if (1 == array_active(strs)) + { + desc_t *desc = array_get(strs, 0); + + if (CMD_VARARG(desc->cmd)) + { + varflag = 1; + } + + if (varflag || CMD_VARIABLE(desc->cmd) || CMD_OPTION(desc->cmd)) + { + argv[argc++] = array_get(cmd_line, i); + } + } + else + { + argv[argc++] = array_get(cmd_line, i); + } + } + + if (argc >= CMD_ARGC_MAX) + { + return CMD_ERR_EXEED_ARGC_MAX; + } + } + + /* 执行命令. */ + if (cmd) + { + *cmd = matched_element; + } + + if (matched_element->daemon) + { + return CMD_SUCCESS_DAEMON; + } + + return (*matched_element->func)(matched_element, vty, argc, argv); +} + +/* 字符串str与数组v中的cmd一一比较,用于保证数组v中只有1个字符串str: + v -- 字符串数组 + str -- 参与比较的字符串 + + return: 相同返回1,其他返回0.*/ +static int32_t _cmd_desc_unique_string(array_t *v, const char *str) +{ + uint32_t i = 0; + desc_t *desc = NULL; + + for(i = 0; i < array_active(v); i++) + { + if (NULL == (desc = array_get(v, i))) + { + continue; + } + + if (0 == strcmp(desc->cmd, str)) + { + return 1; + } + } + + return 0; +} + +/* 比较src是否符合dst的格式,只要部分匹配即可: + src -- 需要检测的字符串 + dst -- 命令中的字符串 + + return: src匹配dst返回dst,否则返回NULL */ +static const char *_cmd_entry_function_desc(const char *src, const char *dst) +{ + if (CMD_VARARG(dst)) + { + return dst; + } + + if (CMD_RANGE(dst)) + { + if (_cmd_range_match (dst, src)) + { + return dst; + } + else + { + return NULL; + } + } + + if (CMD_IPV6(dst)) + { + if (_cmd_ipv6_match(src)) + { + return dst; + } + else + { + return NULL; + } + } + + if (CMD_IPV6_PREFIX(dst)) + { + if (_cmd_ipv6_prefix_match(src)) + { + return dst; + } + else + { + return NULL; + } + } + + if (CMD_IPV4(dst)) + { + if (_cmd_ipv4_match(src)) + { + return dst; + } + else + { + return NULL; + } + } + + if (CMD_IPV4_PREFIX(dst)) + { + if (_cmd_ipv4_prefix_match(src)) + { + return dst; + } + else + { + return NULL; + } + } + + /* Optional or variable commands always match on '?' */ + if (CMD_OPTION(dst) || CMD_VARIABLE(dst)) + { + return dst; + } + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + { + return dst; + } + + if (0 == strncmp(src, dst, strlen(src))) + { + return dst; + } + else + { + return NULL; + } +} + +/* 返回可供补齐的命令字数组: + cmd_line -- 用户输入的命令 + vty -- 输入命令的vty + status -- 匹配结果 + + return: 匹配的字符串数组 */ +static array_t* _cmd_describe_command_real(array_t *cmd_line, vty_t *vty, int32_t *status) +{ + uint32_t i = 0, j = 0; + array_t *cmds = NULL; + array_t *matchs = NULL; + array_t *strs = NULL; + array_t *descs = NULL; + desc_t *desc = NULL; + cmd_element_t *cmd = NULL; + uint32_t index = array_active(cmd_line) - 1; + int32_t ret = 0; + MATCH_TYPE_E match = no_match; + char *cl_str = NULL; + const char *string = NULL; + + /* 将该节点下的所有命令复制一份. */ + cmds = _cmd_cmds_create(vty->node); + + /* 初始化匹配数组. */ + matchs = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); + + /* 先对倒数第2个关键字之前的命令进行匹配. */ + for (i = 0; i < index; i++) + { + if (NULL == (cl_str = array_get(cmd_line, i))) + { + continue; + } + + /* 比较所有cmds中第i个字符串,不匹配会将cmd从cmds中删除 */ + match = _cmd_filter_by_completion(cl_str, cmds, i); + /* 这里基本进不去,不用深究 */ + if (vararg_match == match) + { + array_t *descs = NULL; + uint32_t j = 0; + uint32_t k = 0; + + for (j = 0; j < array_active(cmds); j++) + { + if ((cmd = array_get(cmds, j)) != NULL && (array_active(cmd->strs))) + { + descs = array_get(cmd->strs, array_active(cmd->strs) - 1); + for (k = 0; k < array_active(descs); k++) + { + desc_t *desc = array_get(descs, k); + array_append(matchs, desc, MTYPE_CLI); + } + } + } + + array_append(matchs, &cmd_desc_enter, MTYPE_CLI); + array_free(cmds, MTYPE_CLI); + + qsort(matchs->index, array_active(matchs), sizeof (void *), _cmp_desc); + *status = CMD_SUCCESS; + return matchs; + } + + /* 根据前一轮的比较,把cmds中不是match匹配类型的cmd去掉. */ + ret = _cmd_is_ambiguous(cl_str, cmds, i, match); + if (1 == ret) + { + array_free(cmds, MTYPE_CLI); + array_free(matchs, MTYPE_CLI); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + else if(2 == ret) + { + array_free(cmds, MTYPE_CLI); + array_free(matchs, MTYPE_CLI); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } + + /* 检查最后一个关键字 */ + cl_str = array_get(cmd_line, index); + if (cl_str) + { + match = _cmd_filter_by_completion(cl_str, cmds, index); + } + + /* 得到匹配的关键字列表. */ + for (i = 0; i < array_active(cmds); i++) + { + if (NULL == (cmd = array_get(cmds, i))) + { + continue; + } + + strs = cmd->strs; + + if (cl_str && index >= array_active(strs)) + { + array_get(cmds, i) = NULL; + continue; + } + + /* 如果命令正好相同,直接加入desc_enter字符串. */ + if (NULL == cl_str && index == array_active(strs)) + { + if (!_cmd_desc_unique_string(matchs, cmd_enter)) + { + array_append(matchs, &cmd_desc_enter, MTYPE_CLI); + } + + continue; + } + + descs = array_get(strs, index); + desc = NULL; + + for (j = 0; j < array_active(descs); j++) + { + if (NULL == (desc = array_get(descs, j))) + { + continue; + } + + if (NULL == (string = _cmd_entry_function_desc(cl_str, desc->cmd))) + { + continue; + } + + if (!_cmd_desc_unique_string(matchs, string)) + { + array_append(matchs, desc, MTYPE_CLI); + } + } + } + array_free(cmds, MTYPE_CLI); + + if (NULL == array_get(matchs, 0)) + { + array_free(matchs, MTYPE_CLI); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + /* 将得到的数组按字符串排序. */ + qsort(matchs->index, array_active(matchs), sizeof(void*), _cmp_desc); + *status = CMD_SUCCESS; + return matchs; +} + +/* 计算公共字符串的长度: + matchs -- 字符串数组 + + return: 公共字符串长度 */ +static uint32_t _cmd_common_str(array_t *matchs) +{ + uint32_t i = 0; + uint32_t j = 0; + desc_t *desc = NULL; + uint32_t common_len = 0xffffffff; + char *s1, *s2; + + if (NULL == array_get(matchs, 0) || NULL == array_get(matchs, 1)) + { + return 0; + } + + for (i = 1; i < array_active(matchs); i++) + { + desc = array_get(matchs, i - 1); + s1 = desc->cmd; + desc = array_get(matchs, i); + s2 = desc->cmd; + + for (j = 0; s1[j] && s2[j]; j++) + { + if (s1[j] != s2[j]) + { + break; + } + } + + if (j < common_len) + { + common_len = j; + } + if (0 == common_len) + { + break; + } + } + + return common_len; +} + +/* Interface functions -------------------------------------------------------*/ +/* 命令行相应'?'输入: + cmd_line -- 当前命令行字符串数组 + vty -- 当前终端结构体 + status -- 补齐状态*/ +array_t *cmd_describe_command(array_t *cmd_line, vty_t *vty, int32_t *status) +{ + return _cmd_describe_command_real(cmd_line, vty, status); +} + +/* 命令行相应'TAB'输入: + cmd_line -- 命令行字符串数组 + vty -- 终端结构体 + status -- 补齐状态 + + return: */ +char **cmd_complete_command(array_t *cmd_line, vty_t *vty, int32_t *status) +{ + uint32_t i = 0; + array_t *matchs = NULL; + array_t *temps = NULL; + desc_t *desc = NULL; + bool is_include_arg = FALSE; + char **match_str = NULL; + char buf[COMPLETE_BUF_SIZE] = {0}; + uint32_t common_len = 0; + uint32_t width = 0; + uint32_t index = array_active(cmd_line) - 1; + + /* 获取可用关键字队列. */ + matchs = _cmd_describe_command_real(cmd_line, vty, status); + switch(*status) + { + case CMD_ERR_AMBIGUOUS: + return NULL; + case CMD_ERR_NO_MATCH: + return NULL; + } + + /* 判断匹配是不是有参数 */ + for (i = 0; i < array_active(matchs); i++) + { + if (NULL == (desc = array_get(matchs, i))) + { + continue; + } + + if (CMD_OPTION(desc->cmd) || CMD_VARIABLE(desc->cmd) + || CMD_VARARG(desc->cmd) || CMD_RANGE(desc->cmd) + || CMD_IPV4(desc->cmd) || CMD_IPV4_PREFIX(desc->cmd) + || CMD_IPV6(desc->cmd) || CMD_IPV6_PREFIX(desc->cmd)) + { + is_include_arg = TRUE; + } + } + + /* 只有1个匹配,且不是参数的情况下,直接返回完整字符串. */ + if (NULL == array_get(matchs, 1) && FALSE == is_include_arg) + { + /* Make new matchs. */ + desc = array_get(matchs, 0); + array_free(matchs, MTYPE_CLI); + + matchs = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); + array_append(matchs, XSTRDUP(MTYPE_CLI, desc->cmd), MTYPE_CLI); + match_str = (char **)matchs->index; + array_free_wrapper(matchs, MTYPE_CLI); + + *status = CMD_COMPLETE_FULL_MATCH; + return match_str; + } + + /* 有多个匹配,且不是参数,且存在公共字符串的情况下,返回公共字符串. */ + if (FALSE == is_include_arg && (common_len = _cmd_common_str(matchs))) + { + uint32_t len = 0; + if (NULL == array_get(cmd_line, index)) + { + len = 0; + } + else + { + len = strlen(array_get(cmd_line, index)); + } + + if (len < common_len) + { + char *common_str = NULL; + desc = array_get(matchs, 0); + + common_str = XMALLOC(MTYPE_CLI, common_len + 1); + memcpy(common_str, desc->cmd, common_len); + common_str[common_len] = '\0'; + + array_free(matchs, MTYPE_CLI); + matchs = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); + array_append(matchs, common_str, MTYPE_CLI); + match_str = (char **)matchs->index; + array_free_wrapper(matchs, MTYPE_CLI); + + *status = CMD_COMPLETE_MATCH; + return match_str; + } + } + + /* 计算所有命令字中最长的长度用于打印对其. */ + for (i = 0; i < array_active(matchs); i++) + { + if (NULL == (desc = array_get(matchs, i))) + { + continue; + } + + if (desc->cmd[0] == '\0') + { + continue; + } + + uint32_t len = 0; + + len = strlen(desc->cmd); + if (desc->cmd[0] == '.') + { + len--; + } + + if (width < len) + { + width = len; + } + } + + /* 打印可用命令字. */ + temps = array_init(INIT_MATCHARR_SIZE, MTYPE_CLI); + for (i = 0; i < array_active(matchs); i++) + { + if (NULL == (desc = array_get(matchs, i))) + { + continue; + } + + if (desc->cmd[0] == '\0') + { + continue; + } + + if (!desc->str) + { + snprintf(buf, COMPLETE_BUF_SIZE, " %-s%s", desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); + } + else + { + snprintf(buf, COMPLETE_BUF_SIZE, " %-*s %s%s", width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); + } + + array_append(temps, XSTRDUP(MTYPE_CLI, buf), MTYPE_CLI); + } + + array_append(temps, NULL, MTYPE_CLI); + match_str = (char **)temps->index; + array_free_wrapper(temps, MTYPE_CLI); + + array_free(matchs, MTYPE_CLI); + *status = CMD_COMPLETE_LIST_MATCH; + return match_str; +} + +/* 将命令节点中的命令按字符串排序: + + return: void */ +void cmd_sort_node(void) +{ + uint32_t i = 0; + cmd_node_t *node = NULL; + + for (i = 0; i < array_active(cmd_nodes); i++) + { + if (NULL == (node = array_get(cmd_nodes, i))) + { + continue; + } + + array_t *cmds = node->cmds; + qsort(cmds->index, array_active(cmds), sizeof (void *), _cmp_node); + } +} + +/* 将命令行模式加入总模式节点中: + node -- 命令行节点结构体 + func -- 该节点下的保存配置函数 + + return: void */ +void cmd_install_node(cmd_node_t *node, cmd_save_config_f *func) +{ + array_set(cmd_nodes, node->node, node, MTYPE_CLI); + node->func = func; + node->cmds = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); +} + +/* 安装命令到相应的命令模式节点: + ntype -- 命令行节点 + cmd -- 命令. + + return: void + node: 命令没有检查入参数的有效性,请调用者一定保证入参的正确性.*/ +void cmd_install_element(NODE_TYPE_E ntype, cmd_element_t *cmd) +{ + cmd_node_t *cnode = NULL; + + if (!cmd_nodes) + { + log_err(LOG_CLI, "Command isn't initialied, please check it."); + return; + } + + cnode = array_get(cmd_nodes, ntype); + if (NULL == cnode) + { + log_err(LOG_CLI, "Command node %d doesn't exist, please check it.", ntype); + return; + } + + array_append(cnode->cmds, cmd, MTYPE_CLI); + + if (NULL == cmd->strs) + { + cmd->strs = _cmd_strs_create(cmd->string, cmd->doc); + } + + cmd->str_size = _cmd_descs_size(cmd->strs); +} + +/* 命令行核心初始化: + + return: void */ +void cmd_init(void) +{ + cmd_enter = strdup(""); + cmd_desc_enter.cmd = cmd_enter; + cmd_desc_enter.str = strdup(""); + + /* 初始化所有命令的总节点. */ + cmd_nodes = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); + + /* 默认主机信息. */ + strncpy(host.name, "50100110", FILE_NAME_LEN); + strncpy(host.configfile, "startup-config", FILE_NAME_LEN); + host.version = version_get(); + host.compile = version_date_get(); + host.lines = -1; + + /* 安装命令行模式节点. */ + cmd_install_node(&common_node, NULL); + cmd_install_node(&username_node, NULL); + cmd_install_node(&password_node, NULL); + cmd_install_node(&enable_node, NULL); + cmd_install_node(&config_node, _config_write_host); + /* config node是全局配置,需要注册其他的配置保存函数 */ + config_node.configs = array_init(CONFIG_PRI_COUNT, MTYPE_CLI); + + /* 安转基本命令. */ + cmd_install_element(COMMON_NODE, &show_version_cmd); + cmd_install_element(COMMON_NODE, &exit_node_cmd); + cmd_install_element(COMMON_NODE, &quit_node_cmd); + cmd_install_element(COMMON_NODE, &show_running_config_cmd); + cmd_install_element(COMMON_NODE, &show_log_default_cmd); + cmd_install_element(COMMON_NODE, &show_log_level_cmd); + cmd_install_element(COMMON_NODE, &show_log_cnt_cmd); + cmd_install_element(COMMON_NODE, &show_log_cmd); + + cmd_install_element(ENABLE_NODE, &config_enable_cmd); + cmd_install_element(ENABLE_NODE, &config_terminal_cmd); + cmd_install_element(ENABLE_NODE, &bash_open_cmd); + cmd_install_element(ENABLE_NODE, &terminal_monitor_cmd); + cmd_install_element(ENABLE_NODE, &no_terminal_monitor_cmd); + cmd_install_element(ENABLE_NODE, &config_write_file_cmd); + cmd_install_element(ENABLE_NODE, &id_set_cmd); + cmd_install_element(ENABLE_NODE, &host_ip_set_cmd); + cmd_install_element(ENABLE_NODE, &default_route_set_cmd); + cmd_install_element(ENABLE_NODE, &factory_date_set_cmd); + cmd_install_element(ENABLE_NODE, &deployment_date_set_cmd); + cmd_install_element(ENABLE_NODE, &logoff_cmd); + cmd_install_element(ENABLE_NODE, &enable_test_cmd); + cmd_install_element(ENABLE_NODE, &reboot_cmd); + + cmd_install_element(CONFIG_NODE, &hostname_set_cmd); + cmd_install_element(CONFIG_NODE, &config_log_level_cmd); + cmd_install_element(CONFIG_NODE, &no_config_log_level_cmd); +} + +/* 分解命令每个单词并填入列表,便于于系统命令进行比较: + string -- 命令行字符串 + + return: 命令行每个单词组成的数组,失败返回NULL */ +array_t *cmd_strs_create(const char *string) +{ + const char *cp = NULL; + const char *start = NULL; + char *word = NULL; + int32_t strlen = 0; + array_t *strs = NULL; + + if (NULL == string) + { + return NULL; + } + + cp = string; + + /* 跳过空格. */ + while (isspace((int)*cp) && *cp != '\0') + { + cp++; + } + + /* 如果只有空格则直接返回. */ + if ('\0' == *cp || '!' == *cp || '#' == *cp) + { + return NULL; + } + + strs = array_init(ARRAY_MIN_SIZE, MTYPE_CLI); + + /* 产生单词列表. */ + while (1) + { + start = cp; + + /* 找到word的结束位置. */ + while (!(isspace((int)*cp) || '\r' == *cp || '\n' == *cp) && *cp != '\0') + { + cp++; + } + + /* 产生word. */ + strlen = cp - start; + word = XMALLOC(MTYPE_CLI, strlen + 1); + memcpy(word, start, strlen); + *(word + strlen) = '\0'; + array_append(strs, word, MTYPE_CLI); + + /* 跳过空格. */ + while ((isspace((int)*cp) || '\r' == *cp || '\n' == *cp) && *cp != '\0') + { + cp++; + } + + /* 遇到结束符返回. */ + if ('\0' == *cp) + { + return strs; + } + } +} + +/* 于函数cmd_strs_create对应,释放其分配的空间: + a -- 命令行每个字符串组成的数组 + + return: void */ +void cmd_strs_free(array_t *a) +{ + uint32_t i = 0; + char *cp = NULL; + + if (!a) + { + return; + } + + for (i = 0; i < array_active(a); i++) + { + if ((cp = array_get(a, i)) != NULL) + { + XFREE(MTYPE_CLI, cp); + } + } + + array_free(a, MTYPE_CLI); +} + +/* 从vty执行命令cmd_line: + cmd_line -- 命令字符串 + vty -- 终端结构体 + cmd -- 被执行命令行结构体(OUT) + + return: 执行结果(CMD_xxxx) */ +int32_t cmd_execute_command(array_t *cmd_line, vty_t *vty, cmd_element_t **cmd) +{ + return _cmd_execute_command_real(cmd_line, vty, cmd); +} + +/* 从vty执行命令cmd_line: + vty -- 终端结构体 + + return: 执行结果(CMD_xxxx) */ +int32_t cmd_execute(vty_t *vty) +{ + int ret = CMD_ERR_NO_MATCH; + array_t *cmd_line = NULL; + + /* 分解命令的每个单词到队列. */ + cmd_line = cmd_strs_create(vty->buf); + if (NULL == cmd_line) + { + return CMD_SUCCESS; + } + + ret = cmd_execute_command(cmd_line, vty, NULL); + if (ret != CMD_SUCCESS) + { + switch (ret) + { + case CMD_WARNING: + if (vty->type == VTY_FILE) + vty_out(vty, "Warning...%s", VTY_NEWLINE); + break; + case CMD_ERR_AMBIGUOUS: + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; + case CMD_ERR_NO_MATCH: + vty_out(vty, "%% Unknown command: %s %s", vty->buf, VTY_NEWLINE); + break; + case CMD_ERR_INCOMPLETE: + vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; + } + } + + cmd_strs_free(cmd_line); + + return ret; +} + +/* 注册命令行配置保存函数: + ntype -- 命令行节点类型 + pri -- 优先级 + func -- 配置保存回调函数 + + return: E_xxx */ +int32_t cmd_config_node_config_register(int32_t pri, cmd_save_config_f *func) +{ + cmd_node_t *node = &config_node; + + /* 参数检查 */ + if (pri >= CONFIG_PRI_COUNT || !func) + { + return E_BAD_PARAM; + } + + /* 加入列表 */ + array_set(node->configs, pri, func, MTYPE_CLI); + + return 0; +} + +/* 从shell终端读取一行命. */ +char *_vtysh_gets(void) +{ + HIST_ENTRY *last; + struct termios new_setting,init_setting; + + /* readline要求自己释放其返回的buf. */ + if (line_read) + { + free(line_read); + line_read = NULL; + } + + /* 输入密码时不要回显 */ + if (PASSWORD_NODE == vtysh->node) + { + tcgetattr(0, &init_setting); + new_setting = init_setting; + new_setting.c_lflag &= ~ECHO; + tcsetattr(0, TCSANOW, &new_setting); + } + + /* 获取一行命令. */ + //line_read = readline(vtysh_prompt()); + line_read = readline(NULL); + + /* 打开回显 */ + if (PASSWORD_NODE == vtysh->node) + { + tcsetattr(0,TCSANOW, &init_setting); + printf("\r\n"); + } + + /* 没有字符直接返回 */ + if (NULL == line_read) + { + return NULL; + } + + /* 如果命令有效记录历史. */ + if (*line_read && vtysh->node != PASSWORD_NODE && vtysh->node != USERNAME_NODE) + { + using_history(); + last = previous_history(); + if (!last || strcmp (last->line, line_read) != 0) + { + add_history(line_read); + append_history(1, history_file); + } + } + + return line_read; +} + +void *_vtysh_handle(void *arg) +{ + /* CLI主循环 */ + while(1) + { + if (NULL == _vtysh_gets()) + { + continue; + } + + if (strlen(line_read) >= vtysh->max) + { + printh("ERROR: The command is too long\n"); + continue; + } + + strncpy(vtysh->buf, line_read, vtysh->max - 1); + + //printf("Terminal Input:%s\n", vtysh->buf); + vtycmd_send(); + vty_execute(vtysh); + } + + return NULL; +} + +/* commandLine '?'响应函数. */ +int vtysh_rl_question(void) +{ + array_t *cmd_line = NULL; + + cmd_line = cmd_strs_create(rl_line_buffer); + if (NULL == cmd_line) + { + cmd_line = array_init(1, MTYPE_CLI); + array_append(cmd_line, '\0', MTYPE_CLI); + } + else + if (rl_end && isspace((int)rl_line_buffer[rl_end - 1])) + array_append(cmd_line, '\0', MTYPE_CLI); + + vty_question(vtysh, cmd_line); + + cmd_strs_free(cmd_line); + rl_on_new_line(); + return 0; +} + +/* commandLine 'TAB'响应函数. */ +int vtysh_rl_completion(void) +{ + array_t *cmd_line = NULL; + char **match_strs = NULL; + int32_t complete_status = CMD_ERR_NO_MATCH; + + cmd_line = cmd_strs_create(rl_line_buffer); + if (NULL == cmd_line) + { + cmd_line = array_init(1, MTYPE_CLI); + array_append(cmd_line, '\0', MTYPE_CLI); + } + else + if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1])) + array_append(cmd_line, '\0', MTYPE_CLI); + + match_strs = cmd_complete_command(cmd_line, vtysh, &complete_status); + if (NULL == match_strs) + { + cmd_strs_free(cmd_line); + return 0; + } + + if (CMD_COMPLETE_MATCH == complete_status || + CMD_COMPLETE_FULL_MATCH == complete_status) + _vtysh_append_word(cmd_line, match_strs[0], complete_status); + else + { + vty_print_word(vtysh, match_strs); + rl_on_new_line(); + } + + vty_free_match_strs(match_strs); + cmd_strs_free(cmd_line); + + return 0; +} + +/* shell前缀 */ +char *vtysh_prompt(void) +{ + static char buf[100] = {0}; + cmd_node_t *node = NULL; + + node = cmd_node_get(vtysh->node); + + /* 用户密码输入 */ + if (USERNAME_NODE == node->node + || PASSWORD_NODE == node->node) + { + snprintf(buf, sizeof(buf), "%s", node->prompt); + } + else + { + snprintf(buf, sizeof(buf), node->prompt, host.name); + } + + return buf; +} + +/* 初始化readline. */ +void vtysh_readline_init(void) +{ + /* 修改自己的按键操作. */ + rl_bind_key('?', (rl_command_func_t *)vtysh_rl_question); + rl_bind_key('\t', (rl_command_func_t *)vtysh_rl_completion); + //printf("2 %d\r\n", rl_bind_key(CONTROL('D'), (rl_command_func_t *)vtysh_rl_question)); + //printf("2 %d\r\n", rl_bind_key(CONTROL('Z'), (rl_command_func_t *)vtysh_rl_question)); + /* do not append space after completion. It will be appended + * in new_completion() function explicitly. */ + rl_completion_append_character = '\0'; + + read_history(history_file); + clear_history(); +} + +void vtysh_device_save(void) +{ + int32_t fd = 0; + int32_t rv = 0; + + /* 获取文件名. */ + fd = open("device-config", O_RDWR | O_CREAT | O_TRUNC, 0777); + if (-1 == fd) + { + DBG(DBG_M_CLI, "Can't open config file device-config\r\n"); + return; + } + chmod("device-config", 0666); + + rv = write(fd, &device_info, sizeof(device_info)); + if (rv != sizeof(device_info)) + { + DBG(DBG_M_CLI, "Can't write config file device-config\r\n"); + } + + /* 回收资源 */ + close(fd); + + vtysh_config_save_bak(SAVE_DEV_CFG_BAK_FILE); + return; +} + +void vtysh_eth0_save(void) +{ + int32_t fd = 0; + int32_t rv = 0; + uint16_t len = 0; + char *str = NULL; + + /* 获取文件名. */ + fd = open("/etc/network/Eth0Setting", O_RDWR | O_CREAT | O_TRUNC, 0777); + if (-1 == fd) + { + DBG(DBG_M_CLI, "Can't open config file Eth0Setting\r\n"); + return; + } + chmod("/etc/network/Eth0Setting", 0666); + + str = XMALLOC(MTYPE_VTY_TMP, 512); + len += snprintf(str, 512, "ip=%s\n", device_info.host_ip); + len += snprintf(str + len, 512 - len, "netmask=%s\n", device_info.host_mask); + len += snprintf(str + len, 512 - len, "mac=%02x:%02x:%02x:%02x:%02x:%02x\n", device_info.mac[0], device_info.mac[1], + device_info.mac[2], device_info.mac[3], device_info.mac[4], device_info.mac[5]); + len += snprintf(str + len, 512 - len, "gateway=%s\n", device_info.host_gw); + rv = write(fd, str, len); + if (rv != sizeof(device_info)) + { + DBG(DBG_M_CLI, "Can't write config file Eth0Setting\r\n"); + } + + /* 回收资源 */ + XFREE(MTYPE_VTY_TMP, str); + close(fd); + return; +} + +/* 设备基本信息初始化 */ +void vtysh_device_init(void) +{ + int32_t fd = 0; + int32_t rv = 0; + //uint32_t addr = 0; + //struct sockaddr_in server; + + /* 获取文件名. */ + fd = open("device-config", O_RDONLY); + if (fd != -1) + { + /* 有文件的情况下直接读取配置 */ + rv = read(fd, &device_info, sizeof(device_info)); + if (rv <= 0) + { + log_err(LOG_DEFAULT, "Can't read config file device-config."); + } + close(fd); + } + else + { + device_info.id_major = 5010; + device_info.id_minor = 1; + device_info.mac[0] = 0x68;device_info.mac[1] = 0x70;device_info.mac[2] = 0xDC; + device_info.mac[3] = 0x89;device_info.mac[4] = 0x97;device_info.mac[5] = 0x6E; + snprintf(device_info.host_ip, INET_ADDRSTRLEN, DEV_INFO_DEFAULT_IP); + snprintf(device_info.host_mask, INET_ADDRSTRLEN, DEV_INFO_DEFAULT_MASK); + snprintf(device_info.host_gw, INET_ADDRSTRLEN, DEV_INFO_DEFAULT_GW); + device_info.factory_date = 1685808000; + device_info.deployment_date = 1685808000; + snprintf(device_info.dev_type, FILE_NAME_LEN, "GOM%04d", device_info.id_major); + + + vtysh_device_save(); + vtysh_eth0_save(); +#if 0 + /* 没有配置文件的时候,使用默认配置设置网卡 */ + if (inet_aton(device_info.host_ip, &server.sin_addr) < 0) + { + log_err(LOG_DEFAULT, "ip inet_aton host_ip is error!\r\n"); + } + addr = server.sin_addr.s_addr; + sockunion_ip_set("eth0", addr); + + if (inet_aton(device_info.host_mask, &server.sin_addr) < 0) + { + log_err(LOG_DEFAULT, "mask inet_aton host_mask is error!\r\n"); + } + addr = server.sin_addr.s_addr; + sockunion_mask_set("eth0", addr); + + if (inet_aton(device_info.host_gw, &server.sin_addr) < 0) + { + log_err(LOG_DEFAULT, "gateway inet_aton host_gw is error!\r\n"); + } + addr = server.sin_addr.s_addr; + sockunion_gw_set("eth0", addr, addr); +#endif + } + + return; +} + +/* 初始化shell. */ +void vtysh_init(void) +{ + vtysh_readline_init(); + vty_init(); + vtysh_device_init(); + + /* Make vty structure. */ + vtysh = vty_create(); + vtysh->type = VTY_SHELL; + //vtysh->node = CONFIG_NODE; + vtysh->node = USERNAME_NODE; + + if (pthread_mutex_init(&m_config_mutex, NULL) != 0) + { + log_err(LOG_DEFAULT, "ERROR at mutex init return %s!", safe_strerror(errno)); + return; + } +} + +/* 配置恢复并开启shell线程 */ +void vtysh_config_recovery(void) +{ + char *config_file = NULL; + FILE *file = NULL; + char *buf = NULL; + + /* 获取文件名. */ + config_file = host.configfile; + file = fopen(config_file, "r"); + if (!file) + { + log_err(LOG_DEFAULT, "Can't open config file %s!", host.configfile); + vtysh->node = USERNAME_NODE; + return; + } + + /* 配置恢复 */ + buf = XMALLOC(MTYPE_CLI, VTYSH_CONFIG_R_BUF); + while (!feof(file)) + { + if (NULL == fgets(buf, VTYSH_CONFIG_R_BUF, file)) + { + break; + } + strncpy(vtysh->buf, buf, vtysh->max - 1); + vty_execute(vtysh); + } + XFREE(MTYPE_CLI, buf); + + /* 回收资源 */ + fclose(file); + vtysh->node = USERNAME_NODE; +} + +int32_t vtysh_config_save_bak(int idx) +{ + char src_file[128] = {0}; + char bak_file[128] = {0}; + char cmd[128] = {0}; + snprintf(bak_file, 127, "%s/bak", DEVICE_RUN_PATH); + + if (idx == SAVE_DEV_CFG_BAK_FILE) + { + snprintf(src_file, 127, "%s/device-config", DEVICE_RUN_PATH); + snprintf(cmd, 127, "cp -rf %s %s", src_file, bak_file); + system(cmd); + system("sync"); + } + else if (idx == SAVE_STA_CFG_BAK_FILE) + { + snprintf(src_file, 127, "%s/startup-config", DEVICE_RUN_PATH); + snprintf(cmd, 127, "cp -rf %s %s", src_file, bak_file); + system(cmd); + system("sync"); + } + return 0; +} + +int32_t vtysh_config_save(void) +{ + uint32_t i = 0; + int fd; + cmd_node_t *node = NULL; + char *config_file = NULL; + char *config_file_tmp = NULL; + char *config_file_sav = NULL; + char buf[TIME_STR_LEN] = {0}; + vty_t *file_vty = NULL; + + /* 获取文件名. */ + config_file = host.configfile; + pthread_mutex_lock(&m_config_mutex); + + config_file_sav = XMALLOC(MTYPE_CLI, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1); + strcpy(config_file_sav, config_file); + strcat(config_file_sav, CONF_BACKUP_EXT); + + config_file_tmp = XMALLOC(MTYPE_CLI, strlen(config_file) + 8); + sprintf(config_file_tmp, "%s.XXXXXX", config_file); + + /* 打开临时文件写入配置. */ + fd = open(config_file_tmp, O_RDWR | O_CREAT ,0777); + if (fd < 0) + { + printh("Can't open configuration file %s.\r\n", config_file_tmp); + XFREE(MTYPE_CLI, config_file_tmp); + XFREE(MTYPE_CLI, config_file_sav); + pthread_mutex_unlock(&m_config_mutex); + return E_ERROR; + } + + /* Make vty for configuration file. */ + file_vty = vty_create(); + file_vty->fd = fd; + file_vty->type = VTY_FILE; + + /* Config file header print. */ + vty_out(file_vty, "!\n! Zebra configuration saved from vty\n"); + if (time_string(0, buf, sizeof(buf)) != 0) + vty_out(file_vty, "! %s\n", buf); + vty_out(file_vty, "!\n"); + + for (i = 0; i < array_active(cmd_nodes); i++) + if ((node = array_get(cmd_nodes, i)) && node->func) + { + if ((*node->func)(file_vty)) + vty_out(file_vty, "!\n"); + } + vty_close(file_vty); + + if (rename(config_file, config_file_sav) != 0) + { + printh("Can't backup configuration file %s: %s (%d).\r\n", config_file_sav, safe_strerror(errno), errno); + } + if (rename(config_file_tmp, config_file) != 0) + { + printh("Can't save configuration file %s: %s (%d).\r\n", config_file, safe_strerror(errno), errno); + } + + if (chmod(config_file, 0666) != 0) + { + printh("Can't chmod configuration file %s: %s (%d).\r\n", config_file, safe_strerror(errno), errno); + } + + if (chmod(config_file_sav, 0666) != 0) + { + printh("Can't chmod backup configuration file %s: %s (%d).\r\n", config_file, safe_strerror(errno), errno); + } + + printh("Configuration saved to %s.\r\n", config_file); + + XFREE(MTYPE_CLI, config_file_tmp); + XFREE(MTYPE_CLI, config_file_sav); + pthread_mutex_unlock(&m_config_mutex); + + vtysh_config_save_bak(SAVE_STA_CFG_BAK_FILE); + + return E_NONE; +} + +int32_t vtysh_host_addr_set(char *addr, char *mask) +{ + uint32_t ip = 0; + uint32_t ip_set = 0; + uint8_t mac[MAC_ADDR_LEN] = {0}; + struct sockaddr_in server; + + memcpy(mac, device_info.mac, MAC_ADDR_LEN); + + /* 转换ip地址 */ + inet_aton(device_info.host_ip, &server.sin_addr); + ip = server.sin_addr.s_addr; + if (inet_aton(addr, &server.sin_addr) < 0) + { + DBG(DBG_M_CLI, "inet_aton ip is error!\r\n"); + return E_ERROR; + } + ip_set = server.sin_addr.s_addr; + /* 比较配置 */ + if (ip != ip_set) + { + mac_generate_from_ip(addr, mac); + vty_reset(); + sockunion_ip_set("eth0", ip_set); + vty_serv_sock_family(NULL, 11000, AF_INET); + } + + inet_aton(device_info.host_mask, &server.sin_addr); + ip = server.sin_addr.s_addr; + if (inet_aton(mask, &server.sin_addr) < 0) + { + DBG(DBG_M_CLI, "inet_aton mask is error!\r\n"); + return E_ERROR; + } + ip_set = server.sin_addr.s_addr; + if (ip != ip_set) + { + sockunion_mask_set("eth0", ip_set); + } + + memcpy(device_info.mac, mac, MAC_ADDR_LEN); + strncpy(device_info.host_ip, addr, INET_ADDRSTRLEN - 1); + strncpy(device_info.host_mask, mask, INET_ADDRSTRLEN - 1); + vtysh_device_save(); + vtysh_eth0_save(); + + return E_NONE; +} + +int32_t vtysh_gateway_set(char *gateway) +{ + uint32_t ip = 0; + uint32_t ip_set = 0; + struct sockaddr_in server; + + inet_aton(device_info.host_gw, &server.sin_addr); + ip = server.sin_addr.s_addr; + if (inet_aton(gateway, &server.sin_addr) < 0) + { + DBG(DBG_M_CLI, "inet_aton gateway is error!\r\n"); + return E_ERROR; + } + ip_set = server.sin_addr.s_addr; + if (ip != ip_set) + { + sockunion_gw_set("eth0", ip_set, ip); + } + + strncpy(device_info.host_gw, gateway, INET_ADDRSTRLEN - 1); + vtysh_device_save(); + vtysh_eth0_save(); + + return E_NONE; +} + +void vtysh_shell_init(void) +{ + struct sched_param param; + pthread_attr_t attr; + int32_t rv = 0; + + /* 配置线程RR调度,优先级50 */ + pthread_attr_init(&attr); + param.sched_priority = 50; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + + rv = pthread_create(&_cmd_pid, &attr, _vtysh_handle, NULL); + if (rv != 0) + { + log_err(LOG_DEFAULT, "vtysh_shell_init can't create pthread %d!", rv); + } + else + { + thread_m_add("CLI", _cmd_pid); + } + pthread_attr_destroy(&attr); + + /* YL_TEST */ + //vty_serv_sock_family(device_info.host_ip, 11000, AF_INET); + //vty_serv_sock_family(NULL, 11000, AF_INET); +} + +int create_client_socket() +{ + int sockfd; + + if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) + { + perror("client socket error"); + return -1; + } + + // 一般显式调用bind函数,以便服务器区分不同客户端 + memset(&cliun, 0, sizeof(cliun)); + cliun.sun_family = AF_UNIX; + strcpy(cliun.sun_path, client_path); + socklen = offsetof(struct sockaddr_un, sun_path) + strlen(cliun.sun_path); + unlink(cliun.sun_path); + if (bind(sockfd, (struct sockaddr *)&cliun, socklen) < 0) { + perror("bind error"); + exit(1); + } + + memset(&serun, 0, sizeof(serun)); + serun.sun_family = AF_UNIX; + strcpy(serun.sun_path, server_path); + socklen = offsetof(struct sockaddr_un, sun_path) + strlen(serun.sun_path); + //if (connect(sockfd, (struct sockaddr *)&serun, sizeof(serun)) < 0) + //{ + // perror("connect error"); + // exit(1); + //} + + printf("[%s %d] Connect pass! %d\n",__FILE__,__LINE__,sockfd); + return sockfd; +} + +void *_vtycmd_recv_handle(void *arg) +{ + char *buf = (char*)&vtycmd->buf_read; + int n; + + while(1) { + memset(vtycmd->buf_read.cmd, 0, 504); + n = read(vtycmd->socket, buf, MAXLINE); + if ( n <= 0 ) + { + printf("the other side has been closed.\n"); + break; + } + + //printf("\nsocket READ %d bytes[%s].node=%d param_num=%d\n", strlen(vtycmd->buf_read.cmd), vtycmd->buf_read.cmd, vtycmd->buf_read.node, vtycmd->buf_read.param_num); + //printf("xxx%s\n", vtycmd->buf_read.cmd); + vty_out(vtysh, "%s\n", vtycmd->buf_read.cmd); + vtysh->node = vtycmd->buf_read.node; + pd_port_node.param_num = vtycmd->buf_read.param_num; + } + + return NULL; +} + +void vtycmd_data_init() +{ + vtycmd = XMALLOC(MTYPE_CLI, sizeof(vtycmd_t)); + if (vtycmd == NULL) + { + log_err(LOG_DEFAULT, "ERROR at XMALLOC return %s!", safe_strerror(errno)); + return; + } + vtycmd->socket = -1; + memset(&vtycmd->buf_read, 0, sizeof(vtycmd_head_t)); + memset(&vtycmd->buf_write, 0, sizeof(vtycmd_head_t)); + + if (pthread_mutex_init(&vtycmd->wr_mutex, NULL) != 0) + { + log_err(LOG_DEFAULT, "ERROR at mutex init return %s!", safe_strerror(errno)); + return; + } + + if (pthread_mutex_init(&vtycmd->rd_mutex, NULL) != 0) + { + log_err(LOG_DEFAULT, "ERROR at mutex init return %s!", safe_strerror(errno)); + return; + } +} + +void vtycmd_init() +{ + struct sched_param param; + pthread_attr_t attr; + pthread_t pid; + int32_t rv = 0; + + vtycmd_data_init(); + + vtycmd->socket = create_client_socket(); + + + /* 配置线程RR调度,优先级50 */ + pthread_attr_init(&attr); + param.sched_priority = 50; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + + rv = pthread_create(&pid, &attr, _vtycmd_recv_handle, NULL); + if (rv != 0) + { + log_err(LOG_DEFAULT, "vtysh_shell_init can't create pthread %d!", rv); + } + else + { + thread_m_add("CLI", pid); + } + pthread_attr_destroy(&attr); +} + +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/common.c b/app/lib/m_management/common.c new file mode 100755 index 0000000..d9e52af --- /dev/null +++ b/app/lib/m_management/common.c @@ -0,0 +1,643 @@ +/****************************************************************************** + * file lib/management/common.c + * author YuLiang + * version 1.0.0 + * date 14-Sep-2021 + * brief This file provides all the common operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ +#include +#include +#include +#include +#include + +/* 用户代码头文件. */ +#include "vty.h" + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ +/* 时间缓存结构体. */ +typedef struct _time_cache +{ + time_t last; /* 最后一次时间缓存数据. */ + size_t len; /* buf中数据的长度. */ + char buf[TIME_STR_LEN]; /* 时间数据转化的字符串. */ +} time_cache_t; + +/* Private variables ---------------------------------------------------------*/ +static struct timeval sd_start, sd_end; + +/* Private function prototypes -----------------------------------------------*/ +extern void pd_wdg_clr(void); + +/* Internal functions --------------------------------------------------------*/ +/* 在时间后面根据precision长度,添加更加精确的时间字符串. */ +static size_t _time_string_prec_add(char *buf, size_t len, int32_t precision, struct timeval *clock) +{ + static const int divisor[] = {0, 100000, 10000, 1000, 100, 10, 1}; + char *p = NULL; + + /* 最多统计到us */ + if (precision > 6) + precision = 6; + + p = buf + len + 1 + precision; + *p-- = '\0'; + + clock->tv_usec /= divisor[precision]; + do + { + *p-- = '0' + (clock->tv_usec % 10); + clock->tv_usec /= 10; + } + while(--precision > 0); + + *p = '.'; + return len + 1 + precision; +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 将字符串转为小写. + param: str -- 需要转换的字符转 + return: 转换后的字符串 */ +char *str_to_lower(char *str) +{ + uint32_t i = 0; + + if (str != NULL) + { + for(i = 0; str[i] != '\0'; i++) + { + str[i] = tolower(str[i]); + } + } + + return str; +} + +/* description: 去掉字符串中的多余空格. + param: str -- 需要转换操作的字符转 + omit_end -- omit_end(TRUE)表示会将字符串尾部的空格全部消除 + omit_end(FALSE)表示如果尾部有空格,允许保留1个空格 + return: 操作后的字符串 */ +char *str_omit_space(char *str, bool omit_end) +{ + char *front = str; + char *last = str; + + if (NULL == str) + { + return NULL; + } + + while(' ' == *front) + front++; + + while((*front) != '\0') + { + if (' ' == *front) + { + while((*++front) == ' '); + + if ('\0' == *front && omit_end) + ; + else + { + *last = ' '; + last++; + } + } + else + { + *last = *front; + front++; + last++; + } + } + + *last = '\0'; + return str; +} + +/* description: 根据传入的精度precision,将时间字符串填到buf. + param: precision -- 精度,几位小数 + buf -- 存储转换后字符串的buffer + buflen -- buf的大小 + return: 返回字符串长度,如果失败,返回0 */ +size_t time_string(int32_t precision, char *buf, size_t buflen) +{ + static time_cache_t cache; + struct timeval clock; + + /* 获取时间数据clock. */ + gettimeofday(&clock, NULL); + + /* 更新时间到字符串,cache是静态的,如果两次time_string调用在1秒之内,将沿用上次的cache. */ + if (cache.last != clock.tv_sec) + { + struct tm *tm = NULL; + cache.last = clock.tv_sec; + tm = localtime(&cache.last); + cache.len = strftime(cache.buf, sizeof(cache.buf), "%Y/%m/%d %H:%M:%S", tm); + } + + if (buflen > cache.len) + { + memcpy(buf, cache.buf, cache.len); + + /* 计算秒之后的精度. */ + if ((precision > 0) && (buflen > cache.len + 1 + precision)) + return _time_string_prec_add(buf, cache.len, precision, &clock); + + buf[cache.len] = '\0'; + return cache.len; + } + + /* buf太小,无法生成字符串. */ + if (buflen > 0) + buf[0] = '\0'; + + return 0; +} + +/* description: 获取错误代码字符串. + param: errnum -- 错误代码 + return: 获取的字符串 */ +const char *safe_strerror(int errnum) +{ + const char *s = strerror(errnum); + return (s != NULL) ? s : "Unknown error"; +} + +/* description: 按16进制打印buf数据. + param: buf -- 数据 + len -- 数据长度 + return: */ +void buf_print(char *buf, int32_t len) +{ + int32_t i = 0; + + for(i = 0; i < len;) + { + printh("%02x ", (uint8_t)buf[i++]); + if(0 == i % 32) + { + printh("\r\n"); + } + } + if(i % 32 != 0) + { + printh("\r\n"); + } + + return; +} + +/* description: 通过 ip 生成 mac 地址. + param: ip_str -- 输入 ip 地址 + mac -- 输出 mac 地址 + return: E_XXXX */ +int32_t mac_generate_from_ip(char *ip_str, uint8_t *mac) +{ + struct sockaddr_in server; + uint32_t ip = 0; + uint32_t temp = 0; + + if (NULL == ip_str || NULL == mac) + { + return E_BAD_PARAM; + } + + /* 将 ip_str 转为数字. */ + if (inet_aton(ip_str, &server.sin_addr) < 0) + { + DBG(DBG_M_CLI, "inet_aton ip is error!\r\n"); + return E_BAD_PARAM; + } + ip = server.sin_addr.s_addr; + + /* 产生 mac. */ + srand(time(NULL)); + temp = rand(); + mac[0] = 0x68; + mac[1] = temp & 0xff; + mac[2] = (temp >> 8) & 0xff; + mac[3] = (temp >> 16) & 0xff; + mac[4] = (temp >> 24) & 0xff; + mac[5] = (ip >> 24) & 0xff; + + return E_NONE; +} + +/* description: 计算CRC16/MODBUS. + param: data -- 数据 + size -- 数据长度 + return: 计算的crc值 */ +uint16_t crc16(uint8_t *data, uint16_t size) +{ + uint16_t crc = 0xFFFF; + uint8_t i = 0; + + while(size--) + { + crc = crc ^ *data++; + for(i = 0; i < 8; i++) + { + if ((crc & 0x0001) > 0) + { + crc = crc >> 1; + crc = crc ^ 0xa001; + } + else + crc = crc >> 1; + } + } + + return crc; +} + +void invert_uint8(unsigned char *dest_buf, unsigned char *src_buf) +{ + int i; + unsigned char tmp[4]; + tmp[0] = 0; + + for (i = 0; i < 8; i++) + { + if (src_buf[0] & (1 << i)) + tmp[0] |= 1 << (7 - i); + } + dest_buf[0] = tmp[0]; +} + +void invert_uint16(unsigned short *dest_buf, unsigned short *src_buf) +{ + int i; + unsigned short tmp[4]; + tmp[0] = 0; + + for (i = 0; i < 16; i++) + { + if (src_buf[0] & (1 << i)) + tmp[0] |= 1 << (15 - i); + } + dest_buf[0] = tmp[0]; +} + +uint16_t crc16_modbus(uint8_t *data, uint16_t size) +{ + unsigned short wCRCin = 0xFFFF; + unsigned short wCPoly = 0x8005; + unsigned char wChar = 0; + + while (size--) + { + wChar = *(data++); + invert_uint8(&wChar, &wChar); + wCRCin ^= (wChar << 8); + int i = 0; + for (i = 0; i < 8; i++) + { + if (wCRCin & 0x8000) + wCRCin = (wCRCin << 1) ^ wCPoly; + else + wCRCin = wCRCin << 1; + } + } + invert_uint16(&wCRCin, &wCRCin); + return (wCRCin); +} + +/* description: 时间统计开始. + param: + return: */ +void speed_detection_stat(void) +{ + gettimeofday(&sd_start, NULL); +} + +/* description: 时间统计结束. + param: + return: */ +void speed_detection_end(void) +{ + float timeuse; + + gettimeofday(&sd_end, NULL); + + timeuse = 1000000 * (sd_end.tv_sec - sd_start.tv_sec) + sd_end.tv_usec - sd_start.tv_usec; + timeuse /= 1000000; + printh("Used Time:%f\r\n", timeuse); +} + +/* description: 通用打印函数. + param: + return: */ +int printh(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vprintf(format, args); + vty_print(format, args); + va_end(args); + + return 0; +} + +/* description: 获取int16_t类型的版本号. + param: + return: int16_t类型的版本号 */ +uint16_t sofrware_version_get(void) +{ + char version_str[16]; + char *str = NULL; + char *p = NULL; + uint16_t version = 0; + + snprintf(version_str, 16, "%s", version_get()); + str = strtok_r(version_str, ".", &p); + while(str != NULL) + { + version = version << 8; + version |= (uint8_t)(atoi(str)); + str = strtok_r(NULL, ".", &p); + } + + return version; +} + +/* description: 根据传入的字符串转换成mac地址. + param: + return: */ +int32_t str_to_mac(char *mac_str, OUT uint8_t *mac) +{ + char *str = NULL; + char *p = NULL; + uint8_t len = 0; + uint8_t i = 0; + + /* 按:分词 */ + str = strtok_r(mac_str, ":", &p); + while(str != NULL) + { + /* 检查mac长度 */ + if (len >= 6) + { + return E_BAD_PARAM; + } + + /* 检查字符串 */ + for(i = 0; str[i] && str[i] != '\0'; i++) + { + if (!((str[i] >= '0' && str[i] <= '9') + || (str[i] >= 'a' && str[i] <= 'f') + || (str[i] >= 'A' && str[i] <= 'F'))) + { + return E_BAD_PARAM; + } + } + + /* 检查数据长度 */ + if (i != 2) + { + return E_BAD_PARAM; + } + mac[len++] = strtol(str, NULL, 16); + + /* 获取下个数据 */ + str = strtok_r(NULL, ":", &p); + } + + return E_NONE; +} + +/* description: 根据传入的字符串转换成设备id. + param: + return: */ +int32_t str_to_id(char *id_str, OUT uint32_t *id) +{ + char *str = NULL; + char *p = NULL; + uint8_t len = 0; + uint8_t i = 0; + + /* 按:分词 */ + str = strtok_r(id_str, ".", &p); + while(str != NULL && len < 2) + { + /* 检查id长度 */ + if (len >= 2) + { + return E_BAD_PARAM; + } + + /* 检查字符串 */ + for(i = 0; str[i] && str[i] != '\0'; i++) + { + if (!(str[i] >= '0' && str[i] <= '9')) + { + return E_BAD_PARAM; + } + } + + id[len++] = strtol(str, NULL, 10); + + /* 获取下个数据 */ + str = strtok_r(NULL, ".", &p); + } + + return E_NONE; +} + +int32_t bitmap_set(uint8_t *buf, int32_t nbit, int32_t buf_len) +{ + if ((NULL == buf) || (nbit >= buf_len * 8)) + { + return E_BAD_PARAM; + } + + buf[nbit / 8] |= 1 << (nbit % 8); + + return E_NONE; +} + +int32_t bitmap_reset(uint8_t *buf, int32_t nbit, int32_t buf_len) +{ + if ((NULL == buf) || (nbit >= buf_len * 8)) + { + return E_BAD_PARAM; + } + + buf[nbit / 8] &= ~(1 << (nbit % 8)); + + return E_NONE; +} + +int32_t is_bitmap_set(uint8_t *buf, int32_t nbit, int32_t buf_len) +{ + if ((NULL == buf) || (nbit >= buf_len * 8)) + { + return FALSE; + } + + return buf[nbit / 8] & (1 << (nbit % 8)); +} + +int32_t time_str_to_long(char *date, char *time, uint32_t *t) +{ + struct tm stm; + int32_t year, month, day, hour, minute,second; + + if (sscanf(date, "%d-%d-%d", &year, &month, &day) != 3) + { + return E_BAD_PARAM; + } + if (sscanf(time, "%d:%d:%d", &hour, &minute, &second) != 3) + { + return E_BAD_PARAM; + } + + stm.tm_year = year - 1900; + stm.tm_mon = month - 1; + stm.tm_mday = day; + stm.tm_hour = hour; + stm.tm_min = minute; + stm.tm_sec = second; + stm.tm_isdst = 0; + *t = mktime(&stm); + + return E_NONE; +} + +uint16_t version_str_to_int(void) +{ + uint32_t version_m = 0; + uint32_t version_s = 0; + uint32_t n = 0; + + n = sscanf(version_get(), "%d.%d", &version_m, &version_s); + if (n != 2) + { + return 0xffff; + } + + return (version_m << 8 | (version_s & 0xff)) & 0xffff; +} + +void time_set(time_t timestamp) +{ + struct tm *p =localtime(×tamp); + struct tm tptr = {0}; + struct timeval tv = {0}; + + tptr.tm_year = p->tm_year; + tptr.tm_mon = p->tm_mon; + tptr.tm_mday = p->tm_mday; + tptr.tm_hour = p->tm_hour; + tptr.tm_min = p->tm_min; + tptr.tm_sec = p->tm_sec; + + tv.tv_sec = mktime(&tptr); + tv.tv_usec = 0; + settimeofday(&tv, NULL); +} + +void reboot_system(int module, BOOT_MSG type) +{ + char *pmsg = NULL; + switch (type) + { + case BOOT_LOCAL_SERVER_IP_CHANGE: + pmsg = "Debug Tool has changed server ip."; + break; + case BOOT_LOCAL_SERVER_PORT_CHANGE: + pmsg = "Debug Tool has changed server port."; + break; + case BOOT_LOCAL_IP_CHANGE: + pmsg = "Debug Tool has changed device ip."; + break; + case BOOT_LOCAL_HOST_NAME_CHANGE: + pmsg = "Debug Tool has changed hostname."; + break; + case BOOT_LOCAL_RESET: + pmsg = "Debug Tool send reset command."; + break; + case BOOT_LOCAL_ARM_UPGRADE: + pmsg = "Debug Tool send ARM upgrade command."; + break; + case BOOT_LOCAL_FPGA_UPGRADE: + pmsg = "Debug Tool send FPGA upgrade command."; + break; + case BOOT_REMOTE_SERVER_IP_CHANGE: + pmsg = "Remote has changed server ip."; + break; + case BOOT_REMOTE_SERVER_PORT_CHANGE: + pmsg = "Remote has changed server port."; + break; + case BOOT_REMOTE_IP_CHANGE: + pmsg = "Remote has changed device ip."; + break; + case BOOT_REMOTE_HOST_NAME_CHANGE: + pmsg = "Remote has changed hostname."; + break; + case BOOT_REMOTE_RESET: + pmsg = "Remote send reset command."; + break; + case BOOT_SYSTEM_RESET: + pmsg = "System reboot..."; + break; + default: + break; + } + if (pmsg) + { + log_out(module, LOG_LVL_WARN, pmsg); + log_out(module, LOG_LVL_WARN, "Now,start reboot system..."); + system("sync"); + pd_wdg_clr(); + sleep(3); + system("reboot -f"); + } +} + + +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/dbg.c b/app/lib/m_management/dbg.c new file mode 100644 index 0000000..27b9f3f --- /dev/null +++ b/app/lib/m_management/dbg.c @@ -0,0 +1,246 @@ +/****************************************************************************** + * file lib/management/common.c + * author YuLiang + * version 1.0.0 + * date 14-Sep-2021 + * brief This file provides all the debug operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cmd.h" + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +#ifdef CFG_DBG_ON +dbg_module_t _dbg_module[DBG_M_COUNT] = +{ + {DBG_M_DBG, FALSE, "debug"}, + {DBG_M_CLI, FALSE, "cli"}, + {DBG_M_MTIMER, FALSE, "timer"}, + {DBG_M_PROCESS, FALSE, "process"}, + {DBG_M_GPIO, FALSE, "gpio"}, + {DBG_M_PD, FALSE, "pd"}, + {DBG_M_PD_ERR, FALSE, "pd_err"}, + {DBG_M_PD_DAU, FALSE, "pd_dau"}, + {DBG_M_PD_DAU_SEND, FALSE, "pd_dau_send"}, + {DBG_M_PD_DAU_RECV, FALSE, "pd_dau_recv"}, + {DBG_M_PD_DAU_ERR, TRUE, "pd_dau_err"}, + {DBG_M_FIFO, FALSE, "fifo"}, + {DBG_M_FIFO_ERR, FALSE, "fifo_err"}, + {DBG_M_PD_CSG, FALSE, "csg"}, + {DBG_M_PD_CSG_ERR, TRUE, "csg_err"}, + {DBG_M_STORAGE_ERR, TRUE, "stroage"}, + {DBG_M_DEBUG, FALSE, "debug"}, + {DBG_M_PD_CPLD, TRUE, "cpld"}, +}; + +/* Private function prototypes -----------------------------------------------*/ +CMD(debug_on, + debug_on_cmd, + "debug WORD", + "Debug\n" + "Debug module\n") +{ + int32_t i = 0; + + for(i = 0; i < DBG_M_COUNT; i++) + { + if (strncmp(argv[0], _dbg_module[i].desc, strlen(_dbg_module[i].desc))) + { + continue; + } + + dbg_cmd_hander(DBG_CMD_ON, i); + } + + return CMD_SUCCESS; +} + +CMD(no_debug_on, + no_debug_on_cmd, + "no debug WORD", + NO_STR + "Debug\n" + "Debug module\n") +{ + int32_t i = 0; + + for(i = 0; i < DBG_M_COUNT; i++) + { + if (strncmp(argv[0], _dbg_module[i].desc, strlen(argv[0]))) + { + continue; + } + + dbg_cmd_hander(DBG_CMD_OFF, i); + } + + return CMD_SUCCESS; +} + +CMD(no_debug_all, + no_debug_all_cmd, + "no debug", + NO_STR + "Debug\n") +{ + dbg_cmd_hander(DBG_CMD_ALL_OFF, 0); + + return CMD_SUCCESS; +} + +CMD(show_debug_all, + show_debug_all_cmd, + "show debug", + SHOW_STR + "Debug state\n") +{ + int32_t i = 0; + + for(i = 0; i < DBG_M_COUNT; i++) + { + vty_out(vty, "%03d | %-16s %s%s", i, _dbg_module[i].desc, _dbg_module[i].stat ? "on" : "off", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +/* Internal functions --------------------------------------------------------*/ +/* 开指定模块的debug */ +static int32_t _dbg_on(DBG_MODULE_E module) +{ + if (module >= DBG_M_COUNT) + { + return E_BAD_PARAM; + } + + _dbg_module[module].stat = TRUE; + + return E_NONE; +} + +/* 关指定模块的debug */ +static int32_t _dbg_off(DBG_MODULE_E module) +{ + if (module >= DBG_M_COUNT) + { + return E_BAD_PARAM; + } + + _dbg_module[module].stat = FALSE; + + return E_NONE; +} + +/* 关所有模块的debug */ +static int32_t _dbg_all_off(void) +{ + unsigned int i = 0; + + for(i = 0; i < DBG_M_COUNT; i++) + { + _dbg_module[i].stat = FALSE; + } + + return E_NONE; +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 获取当前模块debug状态. + param: module -- 模块ID + return: (TRUE)开启,(FALSE)关闭 */ +int32_t dbg_stat_get(DBG_MODULE_E module) +{ + if (module >= DBG_M_COUNT) + { + return FALSE; + } + + return _dbg_module[module].stat; +} + +/* description: debug模块命令函数分发. + param: module -- 模块ID + return: (E_NONE)成功,(其他)失败 */ +int32_t dbg_cmd_hander(DBG_CMD_E cmd, int32_t module) +{ + switch(cmd) + { + case DBG_CMD_ON: + LD_E_RETURN(DBG_M_DBG, _dbg_on(module)); + break; + case DBG_CMD_OFF: + LD_E_RETURN(DBG_M_DBG, _dbg_off(module)); + break; + case DBG_CMD_ALL_OFF: + LD_E_RETURN(DBG_M_DBG, _dbg_all_off()); + break; + default: + break; + } + + return E_NONE; +} + +/* description: debug模块初始化. + param: + return: */ +void dbg_init(void) +{ + cmd_install_element(COMMON_NODE, &show_debug_all_cmd); + cmd_install_element(ENABLE_NODE, &debug_on_cmd); + cmd_install_element(ENABLE_NODE, &no_debug_on_cmd); + cmd_install_element(ENABLE_NODE, &no_debug_all_cmd); +} + +#else +int32_t dbg_stat_get(DBG_MODULE_E module) +{ + return FALSE; +} +int dbg_cmd_hander(DBG_CMD_E cmd, int32_t module) +{ + return E_NONE; +} +void dbg_init(void) +{ +} +#endif +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/fifo.c b/app/lib/m_management/fifo.c new file mode 100755 index 0000000..e703a17 --- /dev/null +++ b/app/lib/m_management/fifo.c @@ -0,0 +1,275 @@ +/****************************************************************************** + * file lib/management/fifo.c + * author YuLiang + * version 1.0.0 + * date 21-Feb-2023 + * brief This file provides all the fifo operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ + + +/* 用户代码头文件. */ +#include "cmd.h" +#include "fifo.h" + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +static array_t *fifo = NULL; + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +/* 显示 fifo 的使用情况. */ +CMD(show_fifo, + show_fifo_cmd, + "show fifo", + "Show\n" + "Fifo\n") +{ + uint32_t id = 0; + fifo_t *fifo_node = NULL; + + for (id = 0; id < array_active(fifo); id++) + { + fifo_node = array_lookup(fifo, id); + if (NULL == fifo_node) + { + continue; + } + + fifo_show(id); + } + + return CMD_SUCCESS; +} + +/* Interface functions -------------------------------------------------------*/ +/* 初始化 fifo 全局结构 */ +int32_t fifo_init(void) +{ +#if 0 + fifo = array_init(16, MTYPE_FIFO); + if (!fifo) + { + log_err(LOG_FIFO, "ERROR at array init!"); + return E_MEM; + } +#endif + + cmd_install_element(COMMON_NODE, &show_fifo_cmd); + + return E_NONE; +} + +/* 创建 1 个 fifo. */ +int32_t fifo_create(char* name, uint32_t size) +{ + fifo_t *fifo_node = NULL; + + /* 初始化. */ + fifo_node = XMALLOC(MTYPE_FIFO, sizeof(fifo_t)); + if (!fifo_node) + { + return E_MEM; + } + + snprintf(fifo_node->name, FIFO_NAME_LEN, "%s", name); + fifo_node->size = size; + + /* 申请互斥锁, 用于通知读线程读取有效. */ + if (sem_init(&fifo_node->sem, 0, 0) != 0) + { + XFREE(MTYPE_FIFO, fifo_node); + DBG(DBG_M_FIFO_ERR, "%s ERROR at sem init return %s!\r\n", name, safe_strerror(errno)); + return E_SYS_CALL; + } + + /* 申请信号量, 防止多个线程同时操作 fifo. */ + if (pthread_mutex_init(&fifo_node->mutex, NULL) != 0) + { + XFREE(MTYPE_FIFO, fifo_node); + sem_destroy(&fifo_node->sem); + DBG(DBG_M_FIFO_ERR, "%s ERROR at sem init return %s!\r\n", name, safe_strerror(errno)); + return E_SYS_CALL; + } + + /* 申请 fifo 空间. */ + fifo_node->data = XMALLOC(MTYPE_FIFO, sizeof(void *) * size); + if (!fifo_node->data) + { + XFREE(MTYPE_FIFO, fifo_node); + sem_destroy(&fifo_node->sem); + pthread_mutex_destroy(&fifo_node->mutex); + return E_MEM; + } + + /* 添加到全局结构体. */ + return array_append(fifo, fifo_node, MTYPE_FIFO); +} + +/* 往 fifo 中写入一条数据. */ +int32_t fifo_write(uint32_t id, void *data, int32_t len) +{ + int32_t index = 0; + fifo_t *fifo_node = array_get(fifo, id); + void *temp = NULL; + + /* 参数检查. */ + if (!fifo_node) + { + DBG_Q(DBG_M_FIFO_ERR, "#7\r\n"); + return E_NOT_FOUND; + } + + /* 申请数据空间. */ + temp = XMALLOC_Q(MTYPE_FIFO, len); + if (!temp) + { + return E_MEM; + } + memcpy(temp, data, len); + + pthread_mutex_lock(&fifo_node->mutex); + + /* 判断 fifo 是否满了. */ + index = fifo_node->cur + 1; + if (index == fifo_node->size) + { + index = 0; + } + if (index == fifo_node->valid) + { + DBG_Q(DBG_M_FIFO_ERR, "#8 %d\r\n", id); + XFREE(MTYPE_FIFO, temp); + pthread_mutex_unlock(&fifo_node->mutex); + return E_MEM; + } + + /* 数据加入 fifo. */ + fifo_node->data[fifo_node->cur] = temp; + fifo_node->used++; + if (fifo_node->used > fifo_node->max) + { + fifo_node->max = fifo_node->used; + } + fifo_node->cur = index; + sem_post(&fifo_node->sem); + + pthread_mutex_unlock(&fifo_node->mutex); + + return len; +} + +/* 从 fifo 中读取数据, 注意, 只能一个进程进行读取. */ +int32_t fifo_read(uint32_t id, void **data) +{ + fifo_t *fifo_node = array_get(fifo, id); + + /* 参数检查. */ + if (!fifo_node) + { + DBG(DBG_M_FIFO_ERR, "Fifo %d is not found!\r\n", id); + return E_NOT_FOUND; + } + + /* 等待有效数据, 如果有效, 返回数据. */ + while (fifo_node->valid == fifo_node->cur) + { + sem_wait(&fifo_node->sem); + } + + *data = fifo_node->data[fifo_node->valid]; + + return E_NONE; +} + +/* 释放 fifo 数据, 因为节省了一次内存申请, 所以必须手动释放 fifo 数据. */ +int32_t fifo_push(uint32_t id) +{ + uint32_t index = 0; + fifo_t *fifo_node = array_get(fifo, id); + + /* 检查参数. */ + if (!fifo_node) + { + DBG(DBG_M_FIFO_ERR, "Fifo %d is not found!\r\n", id); + return E_NOT_FOUND; + } + + /* 释放数据. */ + if (fifo_node->valid != fifo_node->cur) + { + pthread_mutex_lock(&fifo_node->mutex); + + XFREE(MTYPE_FIFO, fifo_node->data[fifo_node->valid]); + fifo_node->data[fifo_node->valid] = NULL; + + index = fifo_node->valid + 1; + if (index == fifo_node->size) + { + index = 0; + } + + fifo_node->used--; + fifo_node->valid = index; + + pthread_mutex_unlock(&fifo_node->mutex); + } + + return E_NONE; +} + +/* 显示 fifo 的使用情况. */ +void fifo_show(uint32_t id) +{ + fifo_t *fifo_node = NULL; + + fifo_node = array_lookup(fifo, id); + if (NULL == fifo_node) + { + return; + } + + printh("%-32s %-2d %-2d %-2d %-2d %-2d %-2d\r\n", fifo_node->name, id, fifo_node->size, fifo_node->cur, + fifo_node->valid, fifo_node->used, fifo_node->max); +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/memory.c b/app/lib/m_management/memory.c new file mode 100644 index 0000000..f9527fd --- /dev/null +++ b/app/lib/m_management/memory.c @@ -0,0 +1,563 @@ +/****************************************************************************** + * file lib/management/memory.c + * author YuLiang + * version 1.0.0 + * date 14-Sep-2021 + * brief This file provides all the memory operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ +#include +#include + +/* 用户代码头文件. */ +#include "cmd.h" + +/* Private define ------------------------------------------------------------*/ +#define MEM_LOCK pthread_mutex_lock(&mem_mutex) +#define MEM_UNLOCK pthread_mutex_unlock(&mem_mutex) + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +static mem_node_t mem_list_management[] = +{ + {MTYPE_CLI, "Cli"}, + {MTYPE_BUF, "Buffer"}, + {MTYPE_BUF_DATA, "Buffer data"}, + {MTYPE_BUF_TMP, "Buffer temp"}, + {MTYPE_VTY, "Vty"}, + {MTYPE_VTY_TMP, "Vty temp"}, + {MTYPE_VTY_OUT_BUF, "Vty output buffer"}, + {MTYPE_VTY_HIST, "Vty history"}, + {MTYPE_HASH, "Hash"}, + {MTYPE_HASH_BACKET, "Hash Bucket"}, + {MTYPE_HASH_INDEX, "Hash Index"}, + {MTYPE_LOG, "Logging"}, + {MTYPE_MTIMER, "Timer"}, + {MTYPE_THREAD_MONITOR, "Thread monitor"}, + {MTYPE_PREFIX, "Prefix"}, + {MTYPE_THREAD, "Thread"}, + {MTYPE_THREAD_STATS, "Thread stats"}, + {MTYPE_THREAD_MASTER, "Thread master"}, + {MTYPE_THREAD_FUNCNAME, "Thread function name"}, + {MTYPE_FIFO, "fifo"}, + {MTYPE_GPIO, "gpio"}, + { -1, NULL }, +}; + +static mem_node_t mem_list_transceiver[] = +{ + {MTYPE_RECV, "Receive"}, + {MTYPE_SENT, "Sent"}, + {MTYPE_USART, "Usart"}, + {MTYPE_ETH, "Ethernet"}, + { -1, NULL }, +}; + +static mem_node_t mem_list_process[] = +{ + {MTYPE_GIS, "GIS"}, + {MTYPE_DAU, "DAU"}, + {MTYPE_CSG, "CSG"}, + {MTYPE_STORAGE, "STORAGE"}, + {MTYPE_DEBUG, "DEBUG"}, + { -1, NULL }, +}; + +static mem_list_t mlists[] __attribute__ ((unused)) = +{ + {mem_list_management, "MANAGEMENT"}, + {mem_list_transceiver, "TRANSCEIVER"}, + {mem_list_process, "PROCESS"}, + {NULL, NULL}, +}; + +/* 同于统计每个模块申请内存的次数. */ +static struct +{ + const char *name; + int32_t alloc; + uint32_t t_malloc; + uint32_t c_malloc; + uint32_t t_calloc; + uint32_t c_calloc; + uint32_t t_realloc; + uint32_t t_free; + uint32_t c_strdup; +} mstat[MTYPE_MAX]; + +static pthread_mutex_t mem_mutex; + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +/* 查找模块相应的说明字符串: key -- 模块 */ +static const char *_x_desc_lookup(uint32_t key) +{ + const mem_list_t *list = NULL; + const mem_node_t *pnt = NULL; + + for(list = mlists; list->nodes != NULL; list++) + { + for(pnt = list->nodes; pnt->index >= 0; pnt++) + { + if (pnt->index == key) + { + return pnt->format; + } + } + } + + return ""; +} + +/* 打印每个模块的内存使用情况: pri -- log优先级 */ +static void _x_memstats_print(int32_t pri) +{ + mem_list_t *ml = NULL; + + for (ml = mlists; ml->nodes; ml++) + { + mem_node_t *m = NULL; + + log_out(LOG_MEMORY, pri, "Memory utilization in module %s:", ml->name); + for(m = ml->nodes; m->index >= 0; m++) + { + if (m->index && mstat[m->index].alloc) + { + log_out(LOG_MEMORY, pri, " %-30s: %10ld", m->format, (long)mstat[m->index].alloc); + } + } + } +} + +/* 打印内存申请错误信息: fname -- 调用的函数,type -- 模块, size -- 大小 */ +static void __attribute__ ((noreturn)) _x_mem_error(const char *fname, int32_t type, size_t size) +{ + log_err(LOG_MEMORY, "%s : can't allocate memory for '%s' size %d: %s!", + fname, _x_desc_lookup(type), (int)size, safe_strerror(errno)); + + /* 打印每个模块的内存使用请款 */ + _x_memstats_print(LOG_LVL_WARN); + + /* 打印堆栈信息 */ + log_backtrace(LOG_LVL_WARN); + + abort(); +} + +/* 增加内存alloc计数 */ +static void _x_alloc_inc(int32_t type) +{ + MEM_LOCK; + mstat[type].alloc++; + MEM_UNLOCK; +} + +/* 减少内存alloc计数 */ +static void _x_alloc_dec(int32_t type) +{ + MEM_LOCK; + mstat[type].alloc--; + MEM_UNLOCK; +} + +/* 打印内存申请释放的debug信息. */ +static void _x_mtype_log(char *func, void *memory, const char *file, int32_t line, uint32_t type) +{ + //log_debug(LOG_MEMORY, "%s: %s %p %s %d", func, _x_desc_lookup(type), memory, file, line); + log_out(LOG_MEMORY, LOG_LVL_DBG,"%s: %s %p %s %d", func, _x_desc_lookup(type), memory, file, line); +} + +/* Stats querying from users */ +/* Return a pointer to a human friendly string describing + * the byte count passed in. E.g: + * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc. + * Up to 4 significant figures will be given. + * The pointer returned may be NULL (indicating an error) + * or point to the given buffer, or point to static storage. */ +static const char *_x_mtype_memstr(char *buf, size_t len, uint32_t bytes) +{ + int32_t t = 0; + int32_t g = 0; + int32_t m = 0; + int32_t k = 0; + + /* easy cases */ + if (!bytes) + return "0 bytes"; + if (1 == bytes) + return "1 byte"; + +#if 0 + if (sizeof(unsigned long) >= 8) + /* Hacked to make it not warn on ILP32 machines + * Shift will always be 40 at runtime. See below too */ + t = bytes >> (sizeof (unsigned long) >= 8 ? 40 : 0); + else + t = 0; +#endif + + g = bytes >> 30; + m = bytes >> 20; + k = bytes >> 10; + + if (t > 10) + { + /* The shift will always be 39 at runtime. + * Just hacked to make it not warn on 'smaller' machines. + * Static compiler analysis should mean no extra code */ + if (bytes & (1UL << (sizeof(unsigned long) >= 8 ? 39 : 0))) + t++; + snprintf (buf, len, "%4d TiB", t); + } + else if (g > 10) + { + if (bytes & (1 << 29)) + g++; + snprintf (buf, len, "%d GiB", g); + } + else if (m > 10) + { + if (bytes & (1 << 19)) + m++; + snprintf (buf, len, "%d MiB", m); + } + else if (k > 10) + { + if (bytes & (1 << 9)) + k++; + snprintf (buf, len, "%d KiB", k); + } + else + snprintf (buf, len, "%d bytes", bytes); + + return buf; +} + +/* 打印内存使用情况根据系统调用. */ +static int32_t _x_show_memory_mallinfo(vty_t *vty) +{ + struct mallinfo minfo = mallinfo(); + char buf[MTYPE_MEMSTR_LEN] = {0}; + + vty_out(vty, "System allocator statistics:%s", VTY_NEWLINE); + vty_out(vty, " Total heap allocated: %s%s", + _x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena), VTY_NEWLINE); + vty_out(vty, " Holding block headers: %s%s", + _x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd), VTY_NEWLINE); + vty_out(vty, " Used small blocks: %s%s", + _x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks), VTY_NEWLINE); + vty_out(vty, " Used ordinary blocks: %s%s", + _x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks), VTY_NEWLINE); + vty_out(vty, " Free small blocks: %s%s", + _x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks), VTY_NEWLINE); + vty_out(vty, " Free ordinary blocks: %s%s", + _x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks), VTY_NEWLINE); + vty_out(vty, " Ordinary blocks: %ld%s", (unsigned long)minfo.ordblks, VTY_NEWLINE); + vty_out(vty, " Small blocks: %ld%s", (unsigned long)minfo.smblks, VTY_NEWLINE); + vty_out(vty, " Holding blocks: %ld%s", (unsigned long)minfo.hblks, VTY_NEWLINE); + vty_out(vty, "(see system documentation for 'mallinfo' for meaning)%s", VTY_NEWLINE); + + return 1; +} + +static void _x_show_separator(vty_t *vty) +{ + vty_out(vty, "-----------------------------%s", VTY_NEWLINE); +} + +int32_t _x_show_memory_vty(vty_t *vty, mem_node_t *list) +{ + mem_node_t *m = NULL; + int32_t needsep = 0; + + for (m = list; m->index >= 0; m++) + if (0 == m->index) + { + if (needsep) + { + _x_show_separator(vty); + needsep = 0; + } + } + else + { + vty_out(vty, "%-30s: %10d%s", m->format, mstat[m->index].alloc, VTY_NEWLINE); + needsep = 1; + } + + return needsep; +} + +/* 申请内存,并增加相应的模块的申请数: type -- 模块,size -- 大小 */ +static void *_x_malloc (int32_t type, size_t size) +{ + void *memory = NULL; + + memory = malloc(size); + + if (NULL == memory) + _x_mem_error("malloc", type, size); + + memset(memory, 0, size); + + _x_alloc_inc(type); + + return memory; +} + +/* 申请内存,并增加相应的模块的申请数: type -- 模块,size -- 大小 */ +static void *_x_malloc_q (int32_t type, size_t size) +{ + void *memory = NULL; + + memory = malloc(size); + + if (NULL == memory) + _x_mem_error("malloc", type, size); + + _x_alloc_inc(type); + + return memory; +} + +/* Allocate memory as in zmalloc, and also clear the memory. */ +static void *_x_calloc(int32_t type, size_t size) +{ + void *memory = NULL; + + memory = calloc(1, size); + + if (NULL == memory) + _x_mem_error("calloc", type, size); + + memset(memory, 0, size); + + _x_alloc_inc(type); + + return memory; +} + +/* Given a pointer returned by zmalloc or zcalloc, free it and + * return a pointer to a new size, basically acting like realloc(). + * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the + * same type. + * Effects: Returns a pointer to the new memory, or aborts. */ +static void *_x_realloc(int32_t type, void *ptr, size_t size) +{ + void *memory = NULL; + + memory = realloc(ptr, size); + if (NULL == memory) + _x_mem_error("realloc", type, size); + + if (NULL == ptr) + _x_alloc_inc(type); + + return memory; +} + +/* Free memory allocated by z*alloc or zstrdup. + * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the + * same type. + * Effects: The memory is freed and may no longer be referenced. */ +static void _x_free(int32_t type, void *ptr) +{ + if (ptr != NULL) + { + _x_alloc_dec(type); + free(ptr); + } +} + +/* Duplicate a string, counting memory usage by type. + * Effects: The string is duplicated, and the return value must + * eventually be passed to zfree with the same type. The function will + * succeed or abort. */ +static char *_x_strdup(int32_t type, const char *str) +{ + void *dup = NULL; + + dup = strdup(str); + if (dup == NULL) + _x_mem_error("strdup", type, strlen(str)); + + _x_alloc_inc(type); + + return dup; +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 申请内存. + param: file -- 调用的文件 + line -- 调用的行数 + type -- 哪个模块调用 + size -- 分配的大小 + return: 分配的内存地址 */ +void *mtype_x_malloc(const char *file, int32_t line, int32_t type, size_t size) +{ + void *memory = NULL; + + mstat[type].c_malloc++; + mstat[type].t_malloc++; + + memory = _x_malloc(type, size); + _x_mtype_log("x_malloc", memory, file, line, type); + + return memory; +} + +void *mtype_x_malloc_q(const char *file, int32_t line, int32_t type, size_t size) +{ + void *memory = NULL; + + mstat[type].c_malloc++; + mstat[type].t_malloc++; + + memory = _x_malloc_q(type, size); + _x_mtype_log("x_malloc_q", memory, file, line, type); + + return memory; +} + +/* 见mtype_x_malloc. */ +void *mtype_x_calloc(const char *file, int32_t line, int32_t type, size_t size) +{ + void *memory = NULL; + + mstat[type].c_calloc++; + mstat[type].t_calloc++; + + memory = _x_calloc(type, size); + _x_mtype_log("x_calloc", memory, file, line, type); + + return memory; +} + +/* 见mtype_x_malloc. */ +void *mtype_x_realloc(const char *file, int32_t line, int32_t type, void *ptr, size_t size) +{ + void *memory = NULL; + + /* Realloc need before allocated pointer. */ + mstat[type].t_realloc++; + + memory = _x_realloc(type, ptr, size); + + _x_mtype_log("x_realloc", memory, file, line, type); + + return memory; +} + +/* 见mtype_x_malloc. */ +void mtype_x_free(const char *file, int32_t line, int32_t type, void *ptr) +{ + mstat[type].t_free++; + + _x_free(type, ptr); + + _x_mtype_log("x_free", ptr, file, line, type); +} + +/* 见mtype_x_malloc. */ +char *mtype_x_strdup(const char *file, int32_t line, int32_t type, const char *str) +{ + char *memory = NULL; + + mstat[type].c_strdup++; + + memory = _x_strdup(type, str); + + _x_mtype_log("x_strdup", memory, file, line, type); + + return memory; +} + +CMD(show_memory_all, + show_memory_all_cmd, + "show memory", + SHOW_STR + "Memory statistics\n") +{ + mem_list_t *ml = NULL; + int needsep = 0; + + needsep = _x_show_memory_mallinfo(vty); + + for (ml = mlists; ml->nodes; ml++) + { + if (needsep) + _x_show_separator(vty); + needsep = _x_show_memory_vty(vty, ml->nodes); + } + + return CMD_SUCCESS; +} + +void mem_show(vty_t *vty) +{ + mem_list_t *ml = NULL; + int needsep = 0; + + for (ml = mlists; ml->nodes; ml++) + { + if (needsep) + _x_show_separator(vty); + needsep = _x_show_memory_vty(vty, ml->nodes); + } +} + +/* description: memory模块初始化. + param: + return: */ +void mtype_init(void) +{ + cmd_install_element(COMMON_NODE, &show_memory_all_cmd); +} + +/* description: memory模块初始化. + param: + return: */ +void mtype_init_befor(void) +{ + /* 初始化线程锁. */ + pthread_mutex_init(&mem_mutex, NULL); +} + +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/module.mk b/app/lib/m_management/module.mk new file mode 100644 index 0000000..cc46863 --- /dev/null +++ b/app/lib/m_management/module.mk @@ -0,0 +1,3 @@ +local_src := $(patsubst $(SOURCE_DIR)/%,%,$(wildcard $(SOURCE_DIR)/$(subdirectory)/*.c)) + +$(eval $(call make-library,$(subdirectory)/libm_management.a,$(local_src))) \ No newline at end of file diff --git a/app/lib/m_management/mtimer.c b/app/lib/m_management/mtimer.c new file mode 100755 index 0000000..cde6dcf --- /dev/null +++ b/app/lib/m_management/mtimer.c @@ -0,0 +1,245 @@ +/***************************************************************************** + * file lib/management/mtimer.c + * author Yuliang + * version 1.0.0 + * date 22-Sep-2021 + * brief This file provides all the timer related operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "array.h" +#include "mtimer.h" +#include "cmd.h" + +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +static pthread_t mtimer_pid; +static array_t *mtimer_array; +static pthread_mutex_t mtimer_mutex; + +/* Private function prototypes -----------------------------------------------*/ +extern void _mtimer_lock(void); +extern void _mtimer_unlock(void); + +/* 485设备显示. */ +CMD(mtime_show, + mtime_show_cmd, + "show mtime", + SHOW_STR + "mtime\n") +{ + mtimer_t *entry = NULL; + uint32_t i = 0; + + /* 遍历所有外设 */ + _mtimer_lock(); + vty_out(vty, "ID %-32s INR%s", "NAME", VTY_NEWLINE); + for (i = 0; i < array_active(mtimer_array); i++) + { + entry = array_lookup(mtimer_array, i); + if (NULL == entry) + { + continue; + } + + vty_out(vty, "%-02d %-32s %d%s", i, entry->name, entry->interval, VTY_NEWLINE); + } + + _mtimer_unlock(); + return CMD_SUCCESS; +} + +/* Internal functions --------------------------------------------------------*/ +void _mtimer_lock(void) +{ + pthread_mutex_lock(&mtimer_mutex); +} + +void _mtimer_unlock(void) +{ + pthread_mutex_unlock(&mtimer_mutex); +} + +/* 定时器线程. */ +void *_mtimer_process(void *arg) +{ + uint32_t i = 0; + uint32_t t = 0; + mtimer_t *entry = NULL; + + while(1) + { + /* 最小粒度1s */ + sleep(1); + + /* 遍历所有定时器 */ + _mtimer_lock(); + t = time(NULL); + for (i = 0; i < array_active(mtimer_array); i++) + { + entry = array_lookup(mtimer_array, i); + if (NULL == entry) + { + continue; + } + + /* 判断时间有没有到 */ + if (abs(t - entry->time) < entry->interval) + { + continue; + } + + /* 调用回调函数 */ + entry->handle(entry->arg); + + /* 删除定时器 */ + array_unset(mtimer_array, i); + XFREE(MTYPE_MTIMER, entry); + } + _mtimer_unlock(); + } + + return NULL; +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 添加定时器. + param: + return: (E_NONE)成功,(其他)失败 */ +int32_t mtimer_add(MTIMER_CALLBACK func, void *arg, uint16_t interval, char *name) +{ + mtimer_t *timer = NULL; + + _mtimer_lock(); + timer = XMALLOC(MTYPE_MTIMER, sizeof(mtimer_t)); + if (NULL == timer) + { + DBG(DBG_M_MTIMER, "Malloc error!\r\n"); + _mtimer_unlock(); + return E_MEM; + } + + timer->handle = func; + timer->arg = arg; + snprintf(timer->name, DEV_NAME_STR_LEN, "%s", name); + timer->interval = interval; + timer->time = time(NULL); + array_append(mtimer_array, timer, MTYPE_MTIMER); + + _mtimer_unlock(); + return E_NONE; +} + +/* description: 删除定时器. + param: + return: (E_NONE)成功,(其他)失败 */ +int32_t mtimer_del(MTIMER_CALLBACK func, void *arg) +{ + uint32_t i = 0; + mtimer_t *entry = NULL; + + _mtimer_lock(); + /* 遍历数组 */ + for (i = 0; i < array_active(mtimer_array); i++) + { + entry = array_lookup(mtimer_array, i); + if (NULL == entry) + { + continue; + } + + /* 比较数据 */ + if (entry->handle == func && entry->arg == arg) + { + /* 删除定时器 */ + array_unset(mtimer_array, i); + XFREE(MTYPE_MTIMER, entry); + } + } + + _mtimer_unlock(); + return E_NONE; +} + +/* description: 定时器初始化. + param: + return: (E_NONE)成功,(其他)失败 */ +int32_t mtimer_init(void) +{ +#if 0 + struct sched_param param; + pthread_attr_t attr; + pthread_mutexattr_t attr_m; + + /* 初始化用户列表 */ + mtimer_array = array_init(16, MTYPE_MTIMER); + + /* 递归锁 */ + pthread_mutexattr_init(&attr_m); + pthread_mutexattr_settype(&attr_m, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mtimer_mutex, &attr_m); + + /* 配置线程RR调度,优先级50 */ + pthread_attr_init(&attr); + param.sched_priority = 50; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + + /* 创建接收数据线程. */ + if (pthread_create(&mtimer_pid, &attr, _mtimer_process, NULL) != 0) + { + log_err(LOG_DEFAULT, "mtimer_init can't create pthread!"); + } + else + { + thread_m_add("TIMER", mtimer_pid); + } + pthread_attr_destroy(&attr); + +#endif + cmd_install_element(COMMON_NODE, &mtime_show_cmd); + + return E_NONE; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/lib/m_management/sockunion.c b/app/lib/m_management/sockunion.c new file mode 100755 index 0000000..79037db --- /dev/null +++ b/app/lib/m_management/sockunion.c @@ -0,0 +1,605 @@ +/****************************************************************************** + * file lib/management/sockunion.c + * author YuLiang + * version 1.0.0 + * date 14-Sep-2021 + * brief This file provides all the sockunion operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "memory.h" +#include "sockunion.h" + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private typedef -----------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +/* Maskbit. */ +static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff}; + +/* Static structure for IPv4 access_list's master. */ +static access_master_t access_master_ipv4 = +{ + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL, +}; + +#ifdef HAVE_IPV6 +/* Static structure for IPv6 access_list's master. */ +static access_master_t access_master_ipv6 = +{ + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL, +}; +#endif /* HAVE_IPV6 */ + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +/* Malloc prefix structure. */ +static prefix_t *_prefix_new(void) +{ + prefix_t *p = NULL; + + p = XMALLOC(MTYPE_PREFIX, sizeof(prefix_t)); + + return p; +} + +/* Interface functions -------------------------------------------------------*/ +/* Free prefix structure. */ +void prefix_free(prefix_t *p) +{ + XFREE(MTYPE_PREFIX, p); +} + +/* Allocate new prefix_ipv4 structure. */ +prefix_ipv4_t *prefix_ipv4_new(void) +{ + prefix_ipv4_t *p = NULL; + + /* Call prefix_new to allocate a full-size struct prefix to avoid problems + * where the prefix_ipv4_t is cast to struct prefix and unallocated + * bytes were being referenced (e.g. in structure assignments). */ + p = (prefix_ipv4_t *)_prefix_new(); + p->family = AF_INET; + + return p; +} + +#ifdef HAVE_IPV6 +/* Allocate a new ip version 6 route */ +prefix_ipv6_t *prefix_ipv6_new (void) +{ + prefix_ipv6_t *p = NULL; + + /* Allocate a full-size struct prefix to avoid problems with structure + * 8 size mismatches. */ + p = (prefix_ipv6_t *)_prefix_new(); + p->family = AF_INET6; + return p; +} +#endif + +static access_master_t *_access_master_get(uint16_t afi) +{ + if (AFI_IP == afi) + return &access_master_ipv4; +#ifdef HAVE_IPV6 + else if (AFI_IP6 == afi) + return &access_master_ipv6; +#endif /* HAVE_IPV6 */ + + return NULL; +} + +/* If filter match to the prefix then return 1. */ +static int _filter_match_cisco(filter_t *mfilter, prefix_t *p) +{ + filter_cisco_t *filter = NULL; + struct in_addr mask; + uint32_t check_addr = 0; + uint32_t check_mask = 0; + + filter = &mfilter->u.cfilter; + check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr; + + if (filter->extended) + { + masklen2ip(p->prefixlen, &mask); + check_mask = mask.s_addr & ~filter->mask_mask.s_addr; + + if (0 == memcmp(&check_addr, &filter->addr.s_addr, 4) && + 0 == memcmp(&check_mask, &filter->mask.s_addr, 4)) + return 1; + } + else if (0 == memcmp(&check_addr, &filter->addr.s_addr, 4)) + return 1; + + return 0; +} + +/* If filter match to the prefix then return 1. */ +static int _filter_match_zebra(filter_t *mfilter, prefix_t *p) +{ + filter_zebra_t *filter= NULL; + + filter = &mfilter->u.zfilter; + + if (filter->prefix.family == p->family) + { + if (filter->exact) + { + if (filter->prefix.prefixlen == p->prefixlen) + return prefix_match(&filter->prefix, p); + else + return 0; + } + else + return prefix_match(&filter->prefix, p); + } + else + return 0; +} + +/* Lookup access_list from list of access_list by name. */ +access_list_t *access_list_lookup(uint16_t afi, const char *name) +{ + access_list_t *access = NULL; + access_master_t *master = NULL; + + if (NULL == name) + return NULL; + + master = _access_master_get(afi); + if (NULL== master) + return NULL; + + for(access = master->num.head; access; access = access->next) + if (0 == strcmp(access->name, name)) + return access; + + for(access = master->str.head; access; access = access->next) + if (0 == strcmp(access->name, name)) + return access; + + return NULL; +} + +/* Apply access list to object (which should be prefix_t *). */ +FILTER_TYPE_E access_list_apply(access_list_t *access, void *object) +{ + filter_t *filter = NULL; + prefix_t *p = NULL; + + p = (prefix_t *)object; + + if (NULL == access) + return FILTER_DENY; + + for (filter = access->head; filter; filter = filter->next) + { + if (filter->cisco) + { + if (_filter_match_cisco(filter, p)) + return filter->type; + } + else + { + if (_filter_match_zebra(filter, p)) + return filter->type; + } + } + + return FILTER_DENY; +} + +/* Convert masklen into IP address's netmask. */ +void masklen2ip(int masklen, struct in_addr *netmask) +{ + uint8_t *pnt = NULL; + int32_t bit = 0; + int32_t offset = 0; + + memset(netmask, 0, sizeof(struct in_addr)); + pnt = (unsigned char *)netmask; + + offset = masklen / 8; + bit = masklen % 8; + + while(offset--) + *pnt++ = 0xff; + + if (bit) + *pnt = maskbit[bit]; +} + +/* If n includes p prefix then return 1 else return 0. */ +int prefix_match(const prefix_t *n, const prefix_t *p) +{ + int32_t offset = 0; + int32_t shift = 0; + const uint8_t *np = NULL; + const uint8_t *pp = NULL; + + /* If n's prefix is longer than p's one return 0. */ + if (n->prefixlen > p->prefixlen) + return 0; + + /* Set both prefix's head pointer. */ + np = (const u_char *)&n->u.prefix; + pp = (const u_char *)&p->u.prefix; + + offset = n->prefixlen / PNBBY; + shift = n->prefixlen % PNBBY; + + if (shift) + if (maskbit[shift] & (np[offset] ^ pp[offset])) + return 0; + + while (offset--) + if (np[offset] != pp[offset]) + return 0; + + return 1; +} + +/* Convert IPv4 compatible IPv6 address to IPv4 address. */ +static void _sockunion_normalise_mapped(SOCKUNION_U *su) +{ +#ifdef HAVE_IPV6 + struct sockaddr_in sin; + + if (AF_INET6 == su->sa.sa_family && + IN6_IS_ADDR_V4MAPPED(&su->sin6.sin6_addr)) + { + memset(&sin, 0, sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = su->sin6.sin6_port; + memcpy(&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); + memcpy(su, &sin, sizeof(struct sockaddr_in)); + } +#endif /* HAVE_IPV6 */ +} + +int str2sockunion(const char *str, SOCKUNION_U *su) +{ + int ret = 0; + + memset(su, 0, sizeof(SOCKUNION_U)); + + ret = inet_pton(AF_INET, str, &su->sin.sin_addr); + /* Valid IPv4 address format. */ + if (ret > 0) + { + su->sin.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + su->sin.sin_len = sizeof(struct sockaddr_in); +#endif + return 0; + } + +#ifdef HAVE_IPV6 + ret = inet_pton(AF_INET6, str, &su->sin6.sin6_addr); + /* Valid IPv6 address format. */ + if (ret > 0) + { + su->sin6.sin6_family = AF_INET6; +#ifdef SIN6_LEN + su->sin6.sin6_len = sizeof(struct sockaddr_in6); +#endif + return 0; + } +#endif + + return -1; +} + +const char *sockunion2str(SOCKUNION_U *su, char *buf, size_t len) +{ + if (AF_INET == su->sa.sa_family) + return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len); +#ifdef HAVE_IPV6 + else if(AF_INET6 == su->sa.sa_family) + return inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, len); +#endif + + return NULL; +} + +/* Return accepted new socket file descriptor. */ +int sockunion_accept(int sock, SOCKUNION_U *su) +{ + socklen_t len; + int client_sock; + + len = sizeof(SOCKUNION_U); + client_sock = accept(sock, (struct sockaddr*)su, &len); + + _sockunion_normalise_mapped(su); + return client_sock; +} + +int set_nonblocking(int fd) +{ + int flags = 0; + + /* According to the Single UNIX Spec, the return value for F_GETFL should + * never be negative. */ + if ((flags = fcntl(fd, F_GETFL)) < 0) + { + printh("fcntl(F_GETFL) failed for fd %d: %s", fd, safe_strerror(errno)); + return -1; + } + if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) < 0) + { + printh("fcntl failed setting fd %d non-blocking: %s", fd, safe_strerror(errno)); + return -1; + } + + return 0; +} + +/* Utility function of convert between struct prefix <=> union sockunion. */ +prefix_t *sockunion2hostprefix(const SOCKUNION_U *su) +{ + if (su->sa.sa_family == AF_INET) + { + prefix_ipv4_t *p = NULL; + + p = prefix_ipv4_new(); + p->family = AF_INET; + p->prefix = su->sin.sin_addr; + p->prefixlen = IPV4_MAX_BITLEN; + return (prefix_t*)p; + } + +#ifdef HAVE_IPV6 + if (su->sa.sa_family == AF_INET6) + { + prefix_ipv6_t *p = NULL; + + p = prefix_ipv6_new(); + p->family = AF_INET6; + p->prefixlen = IPV6_MAX_BITLEN; + memcpy(&p->prefix, &su->sin6.sin6_addr, sizeof(struct in6_addr)); + return (prefix_t*)p; + } +#endif /* HAVE_IPV6 */ + + return NULL; +} + +char *sockunion_su2str(SOCKUNION_U *su) +{ + char str[SU_ADDRSTRLEN] = {0}; + + switch (su->sa.sa_family) + { + case AF_INET: + inet_ntop(AF_INET, &su->sin.sin_addr, str, sizeof(str)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + inet_ntop(AF_INET6, &su->sin6.sin6_addr, str, sizeof(str)); + break; +#endif /* HAVE_IPV6 */ + } + + return XSTRDUP(MTYPE_PREFIX, str); +} + +/* Make socket from sockunion union. */ +int sockunion_stream_socket(SOCKUNION_U *su) +{ + int32_t sock = 0; + + if (0 == su->sa.sa_family) + su->sa.sa_family = AF_INET_UNION; + + sock = socket(su->sa.sa_family, SOCK_STREAM, 0); + if (sock < 0) + printh("can't make socket sockunion_stream_socket"); + + return sock; +} + +int sockunion_reuseaddr(int sock) +{ + int ret; + int on = 1; + + ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); + if (ret < 0) + { + printh("can't set sockopt SO_REUSEADDR to socket %d", sock); + return -1; + } + + return 0; +} + +/* Bind socket to specified address. */ +int sockunion_bind(int sock, SOCKUNION_U *su, unsigned short port, SOCKUNION_U *su_addr) +{ + int size = 0; + int ret = 0; + + if (AF_INET == su->sa.sa_family) + { + size = sizeof(struct sockaddr_in); + su->sin.sin_port = htons(port); + if (NULL == su_addr) + su->sin.sin_addr.s_addr = htonl(INADDR_ANY); + } +#ifdef HAVE_IPV6 + else if(AF_INET6 == su->sa.sa_family) + { + size = sizeof(struct sockaddr_in6); + su->sin6.sin6_port = htons(port); + if (NULL == su_addr) + memset (&su->sin6.sin6_addr, 0, sizeof(struct in6_addr)); + } +#endif /* HAVE_IPV6 */ + + + ret = bind(sock, (struct sockaddr *)su, size); + if (ret < 0) + printh("xxcan't bind socket : %s\n", safe_strerror(errno)); + + return ret; +} + +int sockunion_ip_set(char *name, unsigned int addr) +{ + int fd = 0; + struct ifreq ifr; + struct sockaddr_in *sin = NULL; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + printh("Ip set socket error!\r\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, name); + sin = (struct sockaddr_in*)&ifr.ifr_addr; + sin->sin_family = AF_INET; + + /* IP地址 */ + sin->sin_addr.s_addr = addr; + if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) + { + close(fd); + printh("Ip set ioctl SIOCSIFADDR error!\r\n"); + return -2; + } + + close(fd); + return 0; +} + +int sockunion_mask_set(char *name, unsigned int mask) +{ + int fd = 0; + struct ifreq ifr; + struct sockaddr_in *sin = NULL; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + printh("socket error\r\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, name); + sin = (struct sockaddr_in*)&ifr.ifr_addr; + sin->sin_family = AF_INET; + + /* 子网掩码 */ + sin->sin_addr.s_addr = mask; + if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) + { + close(fd); + printh("ioctl\r\n"); + return -3; + } + + close(fd); + return 0; +} + +int sockunion_gw_set(char *name, unsigned int gateway, unsigned int gateway_old) +{ + int fd = 0; + struct rtentry rt; + int rv = 0; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + printh("Gateway set socket error!\r\n"); + return -1; + } + + /* Delete existing defalt gateway */ + memset(&rt, 0, sizeof(rt)); + rt.rt_dst.sa_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_dst)->sin_addr.s_addr = 0; + rt.rt_gateway.sa_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_gateway)->sin_addr.s_addr = gateway_old; + rt.rt_genmask.sa_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr = 0; + rt.rt_flags = RTF_UP; + rv = ioctl(fd, SIOCDELRT, &rt); + + /* Set default gateway */ + if ((rv == 0 || errno == ESRCH) && gateway) + { + memset(&rt, 0, sizeof(rt)); + rt.rt_dst.sa_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_dst)->sin_addr.s_addr = 0; + rt.rt_gateway.sa_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_gateway)->sin_addr.s_addr = gateway; + rt.rt_genmask.sa_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr = 0; + rt.rt_flags = RTF_UP | RTF_GATEWAY; + rv = ioctl(fd, SIOCADDRT, &rt); + } + + close(fd); + return rv; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/m_management/thread_monitor.c b/app/lib/m_management/thread_monitor.c new file mode 100644 index 0000000..91f4b39 --- /dev/null +++ b/app/lib/m_management/thread_monitor.c @@ -0,0 +1,125 @@ +/***************************************************************************** + * file lib/management/thread_monitor.c + * author Yuliang + * version 1.0.0 + * date 08-Oct-2021 + * brief This file provides all the thread monitor related operation functions. + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "array.h" +#include "cmd.h" +#include "thread_monitor.h" + +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ +#define THREAD_M_SIZE 32 + +#define THREAD_LOCK pthread_mutex_lock(&thread_mutex) +#define THREAD_UNLOCK pthread_mutex_unlock(&thread_mutex) + +/* Private macro -------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +static pthread_mutex_t thread_mutex; +static array_t *thread_a; + +/* Private function prototypes -----------------------------------------------*/ +extern int pthread_tryjoin_np(pthread_t thread, void **retval); + +/* Internal functions --------------------------------------------------------*/ +CMD(thread_show, + thread_show_cmd, + "show thread", + SHOW_STR + "thread\n") +{ + thread_m_t *node = NULL; + int32_t i = 0; + + for (i = 0; i < thread_a->active; i++) + { + if (thread_a->index[i] != NULL) + { + node = (thread_m_t*)thread_a->index[i]; + /* 当返回不为EBUSY时说明线程退出或者出错了,但是再次查询又会变为EBUSY, + 所以这里一旦设置为FALSE,就无法再变为TRUE了. */ + if (pthread_tryjoin_np(node->pid, NULL) != EBUSY) + { + node->alive = FALSE; + } + vty_out(vty, "%-32s %-2d%s", node->name, node->alive, VTY_NEWLINE); + } + } + vty_out(vty, "%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* Interface functions -------------------------------------------------------*/ +/* description: 添加需要监控的线程. + param: + return: (E_NONE)成功,(其他)失败 */ +int32_t thread_m_add(char *str, pthread_t pid) +{ + thread_m_t *node = NULL; + + node = XMALLOC(MTYPE_THREAD_MONITOR, sizeof(thread_m_t)); + snprintf(node->name, THREAD_M_NAME_LEN, "%s", str); + node->pid = pid; + node->alive = TRUE; + + THREAD_LOCK; + array_append(thread_a, node, MTYPE_THREAD_MONITOR); + THREAD_UNLOCK; + + return E_NONE; +} + +/* description: 线程监控初始化. + param: + return: (E_NONE)成功,(其他)失败 */ +int32_t thread_m_init(void) +{ + /* 初始化线程锁. */ + pthread_mutex_init(&thread_mutex, NULL); + + thread_a = array_init(THREAD_M_SIZE, MTYPE_THREAD_MONITOR); + cmd_install_element(COMMON_NODE, &thread_show_cmd); + + return E_NONE; +} + +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/ diff --git a/app/lib/m_management/vty.c b/app/lib/m_management/vty.c new file mode 100755 index 0000000..3b9ab94 --- /dev/null +++ b/app/lib/m_management/vty.c @@ -0,0 +1,2740 @@ +/****************************************************************************** + * file lib/management/vty.c + * author YuLiang + * version 1.0.0 + * date 09-Oct-2021 + * brief This file provides all the vty operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "sockunion.h" +#include "vty.h" +#include "cmd.h" + +/* Private define ------------------------------------------------------------*/ +#define BUFFER_SIZE_DEFAULT 4096 +#define BUF_MAX_CHUNKS 16 +#define BUF_MAX_FLUSH 131072 + +/* Private macro -------------------------------------------------------------*/ +#define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUF_DATA, (D)) + +/* Private typedef -----------------------------------------------------------*/ +typedef enum +{ + BUFFER_ERROR = -1, /* I/O错误产生. */ + BUFFER_EMPTY = 0, /* 数据发送成功, 并且没有其他数据要发送了. */ + BUFFER_PENDING = 1 /* 数据发送成功, 但是数据并没有发送完. */ +} BUF_STATUS_E; + +/* Private variables ---------------------------------------------------------*/ +static hash_t *cpu_record = NULL; +static struct timeval last_recent_time; +static struct timeval relative_time_base; +struct timeval recent_time; +/* init flag */ +static unsigned short timers_inited; +/* Relative time, since startup */ +static struct timeval relative_time; + +/* Configure lock. */ +static int vty_config; + +static const char vty_backward_char = 0x08; +static const char vty_space_char = ' '; + +thread_master_t *vty_master; + +/* Vector which store each vty structure. */ +array_t *vtyvec; +/* VTY server thread. */ +array_t *vty_serv_thread; +/* user array. */ +array_t *vty_user; + +/* Vty access-class command */ +static char *vty_accesslist_name = NULL; +#ifdef HAVE_IPV6 +/* Vty access-calss for IPv6. */ +static char *vty_ipv6_accesslist_name = NULL; +#endif + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +/* 在buf中添加一个data空间. */ +static buf_data_t *_buf_add(buf_t *b) +{ + buf_data_t *d; + + d = XMALLOC(MTYPE_BUF_DATA, l_offsetof(buf_data_t, data[b->size])); + d->cp = d->sp = 0; + d->next = NULL; + + if (b->tail) + b->tail->next = d; + else + b->head = d; + b->tail = d; + + return d; +} + +/* 创建buf. */ +buf_t *buf_create(size_t size) +{ + buf_t *b = NULL; + + b = XMALLOC(MTYPE_BUF, sizeof(buf_t)); + + if (size) + b->size = size; + else + { + static size_t default_size; + if (!default_size) + { + long pgsz = sysconf(_SC_PAGESIZE); + default_size = (((BUFFER_SIZE_DEFAULT-1)/pgsz+1)*pgsz); + } + b->size = default_size; + } + + return b; +} + +/* 释放所有buf中的空间. */ +void buf_reset(buf_t *b) +{ + buf_data_t *data = NULL; + buf_data_t *next = NULL; + + for (data = b->head; data; data = next) + { + next = data->next; + BUFFER_DATA_FREE(data); + } + b->head = b->tail = NULL; +} + +/* 释放buf_t结构,包括buf空间. */ +void buf_free(buf_t *b) +{ + buf_reset(b); + XFREE(MTYPE_BUF, b); +} + +/* 将数据存入buf. */ +void buf_put(buf_t *b, const void *p, size_t size) +{ + buf_data_t *data = b->tail; + const char *ptr = p; + + /* We use even last one byte of data buffer. */ + while(size) + { + size_t chunk = 0; + + /* If there is no data buffer add it. */ + if (NULL == data || data->cp == b->size) + data = _buf_add(b); + + chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp)); + memcpy((data->data + data->cp), ptr, chunk); + size -= chunk; + ptr += chunk; + data->cp += chunk; + } +} + +/* 设计用于非阻塞socket. 如果数据太大, 该接口并不会全部发送. + 返回0如果数据全部发送, 1表示还有数据没有发送完, -1表示发送 + 产生了致命错误. */ +BUF_STATUS_E buf_flush_available(buf_t *b, int32_t fd) +{ + buf_data_t *d = NULL; + size_t written = 0; + struct iovec iov[BUF_MAX_CHUNKS]; + size_t iovcnt = 0; + size_t nbyte = 0; + + /* 将数据填入iov,最多使用BUF_MAX_CHUNKS个iov,并且不能超过 + BUF_MAX_FLUSH字节. */ + for(d = b->head, iovcnt = 0; + d && (iovcnt < BUF_MAX_CHUNKS) && (nbyte < BUF_MAX_FLUSH); + d = d->next, iovcnt++) + { + iov[iovcnt].iov_base = d->data + d->sp; + nbyte += (iov[iovcnt].iov_len = d->cp - d->sp); + } + + if (!nbyte) + return BUFFER_EMPTY; + + /* 数据输出到终端. */ + if ((ssize_t)(written = writev(fd, iov, iovcnt)) < 0) + { + if (ERRNO_IO_RETRY(errno)) + return BUFFER_PENDING; + + printf("%s: write error on fd %d: %s.\n", __FUNCTION__, fd, safe_strerror(errno)); + return BUFFER_ERROR; + } + + /* 释放被输出到终端的data. */ + while(written > 0) + { + if (!(d = b->head)) + { + printf("%s: corruption detected: buffer queue empty, but written is %lu", __func__, (u_long)written); + break; + } + + if (written < (d->cp - d->sp)) + { + d->sp += written; + return BUFFER_PENDING; + } + + written -= (d->cp-d->sp); + if (!(b->head = d->next)) + b->tail = NULL; + + BUFFER_DATA_FREE(d); + } + + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; +} + +/* 发送数据到终端,仅用于telnet接口 */ +BUF_STATUS_E buf_flush_window(buf_t *b, int fd, int32_t width, int32_t height, + int32_t erase_flag, int32_t no_more_flag) +{ + int32_t nbytes = 0; + int32_t iov_alloc = 0; + int32_t iov_index = 0; + struct iovec *iov = NULL; + struct iovec small_iov[3]; + char more[] = " --More-- "; + char erase[] = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + buf_data_t *data = NULL; + uint32_t column = 0; + + if (!b->head) + return BUFFER_EMPTY; + + /* 获取显示的高度与宽度. */ + if (height < 1) + { + printf("%s called with non-positive window height %d, forcing to 1", __func__, height); + height = 1; + } + else if (height >= 2) + height--; + + if (width < 1) + { + printf("%s called with non-positive window width %d, forcing to 1", __func__, width); + width = 1; + } + + /* 计算发送一屏的数据要使用多少数据块. */ + if (NULL == b->head->next) + { + iov_alloc = sizeof(small_iov)/sizeof(small_iov[0]); + iov = small_iov; + } + else + { + iov_alloc = ((height * (width + 2)) / b->size) + 10; + iov = XMALLOC(MTYPE_BUF_TMP, iov_alloc * sizeof(*iov)); + } + iov_index = 0; + + /* 处理清屏标志. */ + if (erase_flag) + { + iov[iov_index].iov_base = erase; + iov[iov_index].iov_len = sizeof(erase); + iov_index++; + } + + /* 整理发送数据. */ + column = 1; /* 下一个字符显示的列地址. */ + for (data = b->head; data && (height > 0); data = data->next) + { + size_t cp; + + /* 计算数据位置和行数,保证只显示height行(一屏)数据. */ + cp = data->sp; + while ((cp < data->cp) && (height > 0)) + { + if ('\r' == data->data[cp]) + column = 1; + else if (('\n' == data->data[cp]) || (column == width)) + { + column = 1; + height--; + } + else + column++; + + cp++; + } + iov[iov_index].iov_base = (char *)(data->data + data->sp); + iov[iov_index++].iov_len = cp - data->sp; + data->sp = cp; + + /* 以下情况应该不会发生,申请的块应该是足够发送数据的. */ + if (iov_index == iov_alloc) + { + iov_alloc *= 2; + if (iov != small_iov) + { + printf("%s: growing iov array to %d;width %d, height %d, size %lu", + __func__, iov_alloc, width, height, (u_long)b->size); + iov = XREALLOC(MTYPE_BUF_TMP, iov, iov_alloc * sizeof(*iov)); + } + else + { + printf("%s: corruption detected: iov_small overflowed;head %p, tail %p, head->next %p", + __func__, b->head, b->tail, b->head->next); + iov = XMALLOC(MTYPE_BUF_TMP, iov_alloc * sizeof(*iov)); + memcpy(iov, small_iov, sizeof(small_iov)); + } + } + } + + /* 显示" --More-- "提示. */ + if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) + { + iov[iov_index].iov_base = more; + iov[iov_index].iov_len = sizeof(more); + iov_index++; + } + + /* 发送数据. */ + if ((nbytes = writev(fd, iov, iov_index)) < 0) + printf("%s: writev to fd %d failed: %s", __func__, fd, safe_strerror(errno)); + + /* 释放发送过的数据空间. */ + while (b->head && (b->head->sp == b->head->cp)) + { + buf_data_t *del = NULL; + + if (!(b->head = (del = b->head)->next)) + b->tail = NULL; + + BUFFER_DATA_FREE(del); + } + + if (iov != small_iov) + XFREE(MTYPE_BUF_TMP, iov); + + return (nbytes < 0) ? BUFFER_ERROR : (b->head ? BUFFER_PENDING : BUFFER_EMPTY); +} + +/* 持续发送数据, 直到数据发送完, 或者遇到错误,或者被拥塞. */ +BUF_STATUS_E buf_flush_all(buf_t *b, int32_t fd) +{ + BUF_STATUS_E ret = BUFFER_ERROR; + buf_data_t *head = NULL; + size_t head_sp = 0; + + if (!b->head) + return BUFFER_EMPTY; + + head_sp = (head = b->head)->sp; + + while(BUFFER_PENDING == (ret = buf_flush_available(b, fd))) + { + /* 数据不空, 但是没有发送出去,可能是内核buf满了. */ + if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR)) + return ret; + + head_sp = (head = b->head)->sp; + } + + return ret; +} + +/* Lookup and return hash backet in hash. If there is no + * corresponding hash backet and alloc_func is specified, create new + * hash backet. */ +void *hash_get(hash_t *hash, void *data, hash_alloc_f alloc_func) +{ + unsigned int key = 0; + unsigned int index = 0; + void *newdata = 0; + hash_backet_t *backet = NULL; + + key = (*hash->hash_key) (data); + index = key % hash->size; + + for(backet = hash->index[index]; backet != NULL; backet = backet->next) + if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) + return backet->data; + + if (alloc_func) + { + newdata = (*alloc_func)(data); + if (NULL == newdata) + return NULL; + + backet = XMALLOC(MTYPE_HASH_BACKET, sizeof(hash_backet_t)); + backet->data = newdata; + backet->key = key; + backet->next = hash->index[index]; + hash->index[index] = backet; + hash->count++; + return backet->data; + } + + return NULL; +} + +/* gettimeofday wrapper, to keep recent_time updated */ +static int _thread_gettimeofday(struct timeval *tv) +{ + int ret = 0; + + assert(tv); + + if (!(ret = gettimeofday(&recent_time, NULL))) + { + /* init... */ + if (!timers_inited) + { + relative_time_base = last_recent_time = recent_time; + timers_inited = 1; + } + /* avoid copy if user passed recent_time pointer.. */ + if (tv != &recent_time) + *tv = recent_time; + return 0; + } + + return ret; +} + +static int _thread_get_relative(struct timeval *tv) +{ + int ret = 0; + struct timespec tp; + + if (!(ret = clock_gettime(CLOCK_MONOTONIC, &tp))) + { + relative_time.tv_sec = tp.tv_sec; + relative_time.tv_usec = tp.tv_nsec / 1000; + } + + if (tv) + *tv = relative_time; + + return ret; +} +/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO). + And change negative values to 0. */ +static struct timeval _thread_timeval_adjust (struct timeval a) +{ + while(a.tv_usec >= TIMER_SECOND_MICRO) + { + a.tv_usec -= TIMER_SECOND_MICRO; + a.tv_sec++; + } + + while(a.tv_usec < 0) + { + a.tv_usec += TIMER_SECOND_MICRO; + a.tv_sec--; + } + + if (a.tv_sec < 0) + /* Change negative timeouts to 0. */ + a.tv_sec = a.tv_usec = 0; + + return a; +} + +static unsigned long _thread_timeval_elapsed(struct timeval a, struct timeval b) +{ + return ((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + + (a.tv_usec - b.tv_usec); +} + +/* Allocate a new hash. */ +hash_t *_thread_hash_create_size(hash_key_f *hash_key, + hash_cmp_f *hash_cmp, + unsigned int size) +{ + hash_t *hash; + + hash = XMALLOC(MTYPE_HASH, sizeof(hash_t)); + hash->index = XMALLOC(MTYPE_HASH_INDEX, sizeof(hash_backet_t *) * size); + hash->size = size; + hash->hash_key = hash_key; + hash->hash_cmp = hash_cmp; + hash->count = 0; + + return hash; +} + +/* Add a new thread to the list. */ +static void _thread_list_enqueue(thread_list_t *list, thread_t *thread) +{ + thread->next = NULL; + thread->prev = list->tail; + if (list->tail) + list->tail->next = thread; + else + list->head = thread; + + list->tail = thread; + list->count++; +} + +/* Delete a thread from the list. */ +static thread_t *_thread_list_dequeue(thread_list_t *list, thread_t *thread) +{ + if (thread->next) + thread->next->prev = thread->prev; + else + list->tail = thread->prev; + + if (thread->prev) + thread->prev->next = thread->next; + else + list->head = thread->next; + + thread->next = thread->prev = NULL; + list->count--; + + return thread; +} + +/* Thread list is empty or not. */ +static inline int _thread_empty(thread_list_t *list) +{ + return list->head ? 0 : 1; +} + +static long _thread_timeval_cmp(struct timeval a, struct timeval b) +{ + return a.tv_sec == b.tv_sec ? + a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec; +} + +/* Delete top of the list and return it. */ +static thread_t *_thread_list_pull(thread_list_t *list) +{ + if (!_thread_empty(list)) + return _thread_list_dequeue(list, list->head); + + return NULL; +} + +/* Add a new thread to the list. */ +static void _thread_list_add(thread_list_t *list, + thread_t *thread) +{ + thread->next = NULL; + thread->prev = list->tail; + + if (list->tail) + list->tail->next = thread; + else + list->head = thread; + + list->tail = thread; + list->count++; +} + +/* Add a new thread just before the point. */ +static void _thread_list_add_before(thread_list_t *list, + thread_t *point, + thread_t *thread) +{ + thread->next = point; + thread->prev = point->prev; + if (point->prev) + point->prev->next = thread; + else + list->head = thread; + + point->prev = thread; + list->count++; +} + +/* Return remain time in second. */ +unsigned long _thread_timer_remain_second(thread_t *thread) +{ + _thread_get_relative(NULL); + + if (thread->u.sands.tv_sec - relative_time.tv_sec > 0) + return thread->u.sands.tv_sec - relative_time.tv_sec; + else + return 0; +} + +/* Trim blankspace and "()"s */ +static char *_thread_strip_funcname(const char *funcname) +{ + char buff[100] = {0}; + char tmp = 0; + char *ret = NULL; + char *e = NULL; + char *b = buff; + + strncpy(buff, funcname, sizeof(buff)); + buff[sizeof(buff) -1] = '\0'; + e = buff + strlen(buff) - 1; + + /* Wont work for funcname == "Word (explanation)" */ + while (*b == ' ' || *b == '(') + ++b; + while (*e == ' ' || *e == ')') + --e; + e++; + + tmp = *e; + *e = '\0'; + ret = XSTRDUP(MTYPE_THREAD_FUNCNAME, b); + *e = tmp; + + return ret; +} + +/* Get new thread. */ +static thread_t *_thread_get(thread_master_t *m, + u_char type, + thread_func_f *func, + void *arg, + const char* funcname) +{ + thread_t *thread = NULL; + + if (!_thread_empty(&m->unuse)) + { + thread = _thread_list_pull(&m->unuse); + if (thread->funcname) + XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname); + } + else + { + thread = XMALLOC(MTYPE_THREAD, sizeof(thread_t)); + m->alloc++; + } + + thread->type = type; + thread->add_type = type; + thread->master = m; + thread->func = func; + thread->arg = arg; + thread->funcname = _thread_strip_funcname(funcname); + + return thread; +} + +static void *_thread_cpu_record_hash_alloc(cpu_thread_history_t *a) +{ + cpu_thread_history_t *history = NULL; + history = XMALLOC(MTYPE_THREAD_STATS, sizeof(cpu_thread_history_t)); + history->func = a->func; + history->funcname = XSTRDUP(MTYPE_THREAD_FUNCNAME, a->funcname); + return history; +} + +/* Move thread to unuse list. */ +static void _thread_add_unuse(thread_master_t *m, thread_t *thread) +{ + assert(m != NULL && thread != NULL); + assert(NULL == thread->next); + assert(NULL == thread->prev); + assert(THREAD_UNUSED == thread->type); + + _thread_list_add(&m->unuse, thread); + /* XXX: Should we deallocate funcname here? */ +} + +static unsigned int _thread_cpu_record_hash_key(cpu_thread_history_t *a) +{ + return (uintptr_t)a->func; +} + +static int _thread_cpu_record_hash_cmp (const cpu_thread_history_t *a, + const cpu_thread_history_t *b) +{ + return a->func == b->func; +} + +static thread_t *_thread_run(thread_master_t *m, thread_t *thread, thread_t *fetch) +{ + *fetch = *thread; + thread->type = THREAD_UNUSED; + thread->funcname = NULL; /* thread_call will free fetch's copied pointer */ + _thread_add_unuse(m, thread); + return fetch; +} + +/* process a list en masse, e.g. for event thread lists */ +static unsigned int _thread_process(thread_list_t *list) +{ + thread_t *thread = NULL; + unsigned int ready = 0; + + for(thread = list->head; thread; thread = thread->next) + { + _thread_list_dequeue(list, thread); + thread->type = THREAD_READY; + _thread_list_add(&thread->master->ready, thread); + ready++; + } + + return ready; +} + +static struct timeval _timeval_subtract(struct timeval a, struct timeval b) +{ + struct timeval ret; + + ret.tv_usec = a.tv_usec - b.tv_usec; + ret.tv_sec = a.tv_sec - b.tv_sec; + + return _thread_timeval_adjust(ret); +} + +static struct timeval *_thread_timer_wait(thread_list_t *tlist, struct timeval *timer_val) +{ + if (!_thread_empty (tlist)) + { + *timer_val = _timeval_subtract(tlist->head->u.sands, relative_time); + return timer_val; + } + + return NULL; +} + +/* Add all timers that have popped to the ready list. */ +static uint32_t _thread_timer_process(thread_list_t *list, struct timeval *timenow) +{ + thread_t *thread = NULL; + uint32_t ready = 0; + + for (thread = list->head; thread; thread = thread->next) + { + if (_thread_timeval_cmp(*timenow, thread->u.sands) < 0) + return ready; + + _thread_list_dequeue(list, thread); + thread->type = THREAD_READY; + _thread_list_add(&thread->master->ready, thread); + ready++; + } + + return ready; +} + +static int32_t _thread_process_fd(thread_list_t *list, fd_set *fdset, fd_set *mfdset) +{ + thread_t *thread = NULL; + thread_t *next = NULL; + int ready = 0; + + assert(list); + + for (thread = list->head; thread; thread = next) + { + next = thread->next; + + if (FD_ISSET(THREAD_FD(thread), fdset)) + { + assert(FD_ISSET(THREAD_FD(thread), mfdset)); + FD_CLR(THREAD_FD (thread), mfdset); + _thread_list_dequeue(list, thread); + _thread_list_add(&thread->master->ready, thread); + thread->type = THREAD_READY; + ready++; + } + } + + return ready; +} + +void thread_getrusage(RUSAGE_T *r) +{ + _thread_get_relative(NULL); + getrusage(RUSAGE_SELF, &(r->cpu)); + r->real = relative_time; + + /* quagga_get_relative() only updates recent_time if gettimeofday + * based, not when using CLOCK_MONOTONIC. As we export recent_time + * and guarantee to update it before threads are run...*/ + _thread_gettimeofday(&recent_time); +} + +/* We check thread consumed time. If the system has getrusage, we'll +use that to get in-depth stats on the performance of the thread in addition +to wall clock time stats from gettimeofday. */ +void thread_call(thread_t *thread) +{ + unsigned long realtime = 0; + unsigned long cputime = 0; + RUSAGE_T ru; + + /* Cache a pointer to the relevant cpu history thread, if the thread + * does not have it yet. + * + * Callers submitting 'dummy threads' hence must take care that + * thread->cpu is NULL */ + if (!thread->hist) + { + cpu_thread_history_t tmp; + + tmp.func = (int (*)(void*))thread->func; + tmp.funcname = thread->funcname; + + thread->hist = hash_get(cpu_record, + &tmp, + (hash_alloc_f *)_thread_cpu_record_hash_alloc); + } + + GETRUSAGE(&thread->ru); + + (*thread->func)(thread); + + GETRUSAGE(&ru); + + realtime = thread_consumed_time(&ru, &thread->ru, &cputime); + thread->hist->real.total += realtime; + + if (thread->hist->real.max < realtime) + thread->hist->real.max = realtime; + + thread->hist->cpu.total += cputime; + + if (thread->hist->cpu.max < cputime) + thread->hist->cpu.max = cputime; + + ++(thread->hist->total_calls); + thread->hist->types |= (1 << thread->add_type); + + if (realtime > TIMER_SECOND_MICRO * 2) + { + /* We have a CPU Hog on our hands. + * Whinge about it now, so we're aware this is yet another task + * to fix. */ + printf("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)", + thread->funcname, + (unsigned long) thread->func, + realtime/1000, cputime/1000); + } + + XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname); +} + +/* Add new read thread. */ +thread_t *thread_add_read(thread_master_t *m, + thread_func_f *func, + void *arg, + int fd, + const char* funcname) +{ + thread_t *thread = NULL; + + assert(m != NULL); + + if (FD_ISSET(fd, &m->readfd)) + { + printf("There is already read fd [%d].\n", fd); + return NULL; + } + + thread = _thread_get(m, THREAD_READ, func, arg, funcname); + FD_SET(fd, &m->readfd); + thread->u.fd = fd; + _thread_list_enqueue(&m->read, thread); + + return thread; +} + +/* Add new write thread. */ +thread_t *thread_add_write(thread_master_t *m, + thread_func_f *func, + void *arg, + int fd, + const char* funcname) +{ + thread_t *thread = NULL; + + assert(m != NULL); + + if (FD_ISSET(fd, &m->writefd)) + { + printf("There is already write fd [%d]", fd); + return NULL; + } + + thread = _thread_get(m, THREAD_WRITE, func, arg, funcname); + FD_SET(fd, &m->writefd); + thread->u.fd = fd; + _thread_list_add(&m->write, thread); + + return thread; +} + +static thread_t * +thread_add_timer_timeval(thread_master_t *m, + thread_func_f *func, + int type, + void *arg, + struct timeval *time_relative, + const char* funcname) +{ + thread_t *thread = NULL; + thread_list_t *list = NULL; + struct timeval alarm_time; + thread_t *tt = NULL; + + assert(m != NULL); + assert(type == THREAD_TIMER || type == THREAD_BACKGROUND); + assert(time_relative); + + list = (type == THREAD_TIMER) ? &m->timer : &m->background; + thread = _thread_get(m, type, func, arg, funcname); + + /* Do we need jitter here? */ + _thread_get_relative(NULL); + alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec; + alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec; + thread->u.sands = _thread_timeval_adjust(alarm_time); + + /* Sort by timeval. */ + for(tt = list->head; tt; tt = tt->next) + if (_thread_timeval_cmp(thread->u.sands, tt->u.sands) <= 0) + break; + + if (tt) + _thread_list_add_before(list, tt, thread); + else + _thread_list_add(list, thread); + + return thread; +} + + +/* Add timer event thread. */ +thread_t *thread_add_timer(thread_master_t *m, + thread_func_f *func, + void *arg, + long timer, + const char* funcname) +{ + struct timeval trel; + + assert(m != NULL); + + trel.tv_sec = timer; + trel.tv_usec = 0; + + return thread_add_timer_timeval(m, func, THREAD_TIMER, arg, + &trel, funcname); +} + +/* Add timer event thread with "millisecond" resolution */ +thread_t *thread_add_timer_msec(thread_master_t *m, + thread_func_f *func, + void *arg, + long timer, + const char* funcname) +{ + struct timeval trel; + + assert(m != NULL); + + trel.tv_sec = timer / 1000; + trel.tv_usec = 1000*(timer % 1000); + + return thread_add_timer_timeval(m, func, THREAD_TIMER, + arg, &trel, funcname); +} + +/* Add a background thread, with an optional millisec delay */ +thread_t *thread_add_background(thread_master_t *m, + thread_func_f *func, + void *arg, long delay, + const char *funcname) +{ + struct timeval trel; + + assert(m != NULL); + + if (delay) + { + trel.tv_sec = delay / 1000; + trel.tv_usec = 1000 * (delay % 1000); + } + else + { + trel.tv_sec = 0; + trel.tv_usec = 0; + } + + return thread_add_timer_timeval(m, func, THREAD_BACKGROUND, + arg, &trel, funcname); +} + +/* Add simple event thread. */ +thread_t *thread_add_event(thread_master_t *m, + thread_func_f *func, + void *arg, + int val, + const char* funcname) +{ + thread_t *thread = NULL; + + assert(m != NULL); + + thread = _thread_get(m, THREAD_EVENT, func, arg, funcname); + thread->u.val = val; + _thread_list_add(&m->event, thread); + + return thread; +} + +/* Execute thread */ +thread_t *thread_execute(thread_master_t *m, + thread_func_f *func, + void *arg, + int val, + const char* funcname) +{ + thread_t dummy; + + memset(&dummy, 0, sizeof(thread_t)); + + dummy.type = THREAD_EVENT; + dummy.add_type = THREAD_PERFORM; + dummy.master = NULL; + dummy.func = func; + dummy.arg = arg; + dummy.u.val = val; + dummy.funcname = _thread_strip_funcname(funcname); + thread_call(&dummy); + + return NULL; +} + +/* Allocate new thread master. */ +thread_master_t *thread_master_create() +{ + if (cpu_record == NULL) + cpu_record + = _thread_hash_create_size((hash_key_f*)_thread_cpu_record_hash_key, + (hash_cmp_f*)_thread_cpu_record_hash_cmp, + 1011); + + return (thread_master_t*)XMALLOC(MTYPE_THREAD_MASTER, sizeof(thread_master_t)); +} + +unsigned long thread_consumed_time(RUSAGE_T *now, + RUSAGE_T *start, + unsigned long *cputime) +{ + /* This is 'user + sys' time. */ + *cputime = _thread_timeval_elapsed(now->cpu.ru_utime, start->cpu.ru_utime) + + _thread_timeval_elapsed(now->cpu.ru_stime, start->cpu.ru_stime); + + return _thread_timeval_elapsed(now->real, start->real); +} + +/* Cancel thread from scheduler. */ +void thread_cancel(thread_t *thread) +{ + thread_list_t *list = NULL; + + switch(thread->type) + { + case THREAD_READ: + assert(FD_ISSET(thread->u.fd, &thread->master->readfd)); + FD_CLR(thread->u.fd, &thread->master->readfd); + list = &thread->master->read; + break; + case THREAD_WRITE: + assert(FD_ISSET (thread->u.fd, &thread->master->writefd)); + FD_CLR(thread->u.fd, &thread->master->writefd); + list = &thread->master->write; + break; + case THREAD_TIMER: + list = &thread->master->timer; + break; + case THREAD_EVENT: + list = &thread->master->event; + break; + case THREAD_READY: + list = &thread->master->ready; + break; + case THREAD_BACKGROUND: + list = &thread->master->background; + break; + default: + return; + } + + _thread_list_dequeue(list, thread); + thread->type = THREAD_UNUSED; + _thread_add_unuse(thread->master, thread); +} + +/* Fetch next ready thread. */ +thread_t *thread_fetch(thread_master_t *m, thread_t *fetch) +{ + thread_t *thread = NULL; + fd_set readfd; + fd_set writefd; + fd_set exceptfd; + struct timeval timer_val = {.tv_sec = 0, .tv_usec = 0}; + struct timeval timer_val_bg; + struct timeval *timer_wait = &timer_val; + struct timeval *timer_wait_bg = NULL; + + while (1) + { + int32_t num = 0; + + /* Drain the ready queue of already scheduled jobs, before scheduling + * more. */ + if ((thread = _thread_list_pull(&m->ready)) != NULL) + return _thread_run(m, thread, fetch); + + /* To be fair to all kinds of threads, and avoid starvation, we + * need to be careful to consider all thread types for scheduling + * in each quanta. I.e. we should not return early from here on. */ + + /* Normal event are the next highest priority. */ + _thread_process(&m->event); + + /* Structure copy. */ + readfd = m->readfd; + writefd = m->writefd; + exceptfd = m->exceptfd; + + /* Calculate select wait timer if nothing else to do */ + if (0 == m->ready.count) + { + _thread_get_relative(NULL); + timer_wait = _thread_timer_wait(&m->timer, &timer_val); + timer_wait_bg = _thread_timer_wait(&m->background, &timer_val_bg); + + if (timer_wait_bg && + (!timer_wait || (_thread_timeval_cmp(*timer_wait, *timer_wait_bg) > 0))) + timer_wait = timer_wait_bg; + } + + /* 当可用的进程全为0时,不能让timer_wait为NULL,要不让该线程将永远停在select()处. */ + if (!timer_wait) + { + timer_val.tv_sec = 2; + timer_val.tv_usec = 0; + timer_wait = &timer_val; + } + + num = select(FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); + /* Signals should get quick treatment */ + if (num < 0) + { + if (EINTR == errno) + continue; /* signal received - process it */ + + printf("select() error: %s\n", safe_strerror(errno)); + return NULL; + } + + /* Check foreground timers. Historically, they have had higher + * priority than I/O threads, so let's push them onto the ready + * list in front of the I/O threads. */ + _thread_get_relative(NULL); + _thread_timer_process(&m->timer, &relative_time); + + /* Got IO, process it */ + if (num > 0) + { + /* Normal priority read thead. */ + _thread_process_fd(&m->read, &readfd, &m->readfd); + /* Write thead. */ + _thread_process_fd(&m->write, &writefd, &m->writefd); + } + +#if 0 + /* If any threads were made ready above (I/O or foreground timer), + * perhaps we should avoid adding background timers to the ready + * list at this time. If this is code is uncommented, then background + * timer threads will not run unless there is nothing else to do. */ + if ((thread = thread_trim_head (&m->ready)) != NULL) + return thread_run (m, thread, fetch); +#endif + + /* Background timer/events, lowest priority */ + _thread_timer_process(&m->background, &relative_time); + + if ((thread = _thread_list_pull(&m->ready)) != NULL) + return _thread_run(m, thread, fetch); + } +} + +/* Get telnet window size. */ +static int _vty_telnet_option(vty_t *vty, unsigned char *buf, int nbytes) +{ + switch (buf[0]) + { + case SB: + vty->sb_len = 0; + vty->iac_sb_in_progress = 1; + return 0; + break; + case SE: + { + if (!vty->iac_sb_in_progress) + return 0; + + if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (vty->sb_buf[0]) + { + case TELOPT_NAWS: + if (vty->sb_len != TELNET_NAWS_SB_LEN) + printf("RFC 1073 violation detected: telnet NAWS option\ + should send %d characters, but we received %lu.\n", + TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); + else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) + printf("Bug detected: sizeof(vty->sb_buf) %lu < %d, \ + too small to handle the telnet NAWS option.\n", + (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); + else + { + vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); + vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); + } + break; + } + + vty->iac_sb_in_progress = 0; + return 0; + break; + } + default: + break; + } + + return 1; +} + +/* Basic function to write buffer to vty. */ +static void _vty_write(vty_t *vty, const char *buf, size_t nbytes) +{ + /* Should we do buffering here ? And make vty_flush (vty) ? */ + buf_put(vty->out_buf, buf, nbytes); +} + +/* This function redraw all of the command line character. */ +static void _vty_redraw_line(vty_t *vty) +{ + _vty_write(vty, vty->buf, vty->length); + vty->cp = vty->length; +} + +/* Quit print out to the buffer. */ +static void _vty_buffer_reset(vty_t *vty) +{ + buf_reset(vty->out_buf); + vty_prompt(vty); + _vty_redraw_line(vty); +} + +/* Backward character. */ +static void _vty_backward_char(vty_t *vty) +{ + if (vty->cp > 0) + { + vty->cp--; + _vty_write(vty, &vty_backward_char, 1); + } +} + +/* Forward character. */ +static void _vty_forward_char(vty_t *vty) +{ + if (vty->cp < vty->length) + { + _vty_write(vty, &vty->buf[vty->cp], 1); + vty->cp++; + } +} + +/* Move to the beginning of the line. */ +static void _vty_beginning_of_line(vty_t *vty) +{ + while (vty->cp) + _vty_backward_char(vty); +} + +/* Move to the end of the line. */ +static void _vty_end_of_line(vty_t *vty) +{ + while(vty->cp < vty->length) + _vty_forward_char(vty); +} + +/* Kill rest of line from current point. */ +static void _vty_kill_line(vty_t *vty) +{ + unsigned int i = 0; + unsigned int size = 0; + + size = vty->length - vty->cp; + + if (0 == size) + return; + + for (i = 0; i < size; i++) + _vty_write(vty, &vty_space_char, 1); + for (i = 0; i < size; i++) + _vty_write(vty, &vty_backward_char, 1); + + memset(&vty->buf[vty->cp], 0, size); + vty->length = vty->cp; +} + +/* Kill line from the beginning. */ +static void _vty_kill_line_from_beginning(vty_t *vty) +{ + _vty_beginning_of_line(vty); + _vty_kill_line(vty); +} + +/* Print command line history. This function is called from + vty_next_line and vty_previous_line. */ +static void _vty_history_print(vty_t *vty) +{ + unsigned int length = 0; + + _vty_kill_line_from_beginning (vty); + + /* Get previous line from history buffer */ + length = strlen(vty->hist[vty->hp]); + memcpy(vty->buf, vty->hist[vty->hp], length); + vty->cp = vty->length = length; + + /* Redraw current line */ + _vty_redraw_line(vty); +} + +/* Show previous command line history. */ +static void _vty_previous_line(vty_t *vty) +{ + unsigned int try_index = 0; + + try_index = vty->hp; + if (try_index == 0) + try_index = VTY_MAXHIST - 1; + else + try_index--; + + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + _vty_history_print(vty); +} + +/* Show next command line history. */ +static void _vty_next_line(vty_t *vty) +{ + unsigned int try_index = 0; + + if (vty->hp == vty->hindex) + return; + + /* Try is there history exist or not. */ + try_index = vty->hp; + if (try_index == (VTY_MAXHIST - 1)) + try_index = 0; + else + try_index++; + + /* If there is not history return. */ + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + _vty_history_print (vty); +} + +/* Escape character command map. */ +static void _vty_escape_map (unsigned char c, vty_t *vty) +{ + switch (c) + { + case ('A'): + _vty_previous_line (vty); + break; + case ('B'): + _vty_next_line(vty); + break; + case ('C'): + _vty_forward_char(vty); + break; + case ('D'): + _vty_backward_char (vty); + break; + default: + break; + } + + /* Go back to normal mode. */ + vty->escape = VTY_NO_ESCAPE; +} + +/* Backward word. */ +static void _vty_backward_word(vty_t *vty) +{ + while(vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + _vty_backward_char(vty); + + while(vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + _vty_backward_char(vty); +} + +/* Forward word. */ +static void _vty_forward_word(vty_t *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + _vty_forward_char(vty); + + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + _vty_forward_char(vty); +} + +/* Delete a charcter at the current point. */ +static void _vty_delete_char(vty_t *vty) +{ + unsigned int i = 0; + unsigned int size = 0; + + if (vty->length == 0) + return; + + if (vty->cp == vty->length) + return; + + size = vty->length - vty->cp; + + vty->length--; + memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); + vty->buf[vty->length] = '\0'; + + _vty_write(vty, &vty->buf[vty->cp], size - 1); + _vty_write(vty, &vty_space_char, 1); + + for (i = 0; i < size; i++) + _vty_write(vty, &vty_backward_char, 1); +} + +/* Delete a character before the point. */ +static void _vty_delete_backward_char(vty_t *vty) +{ + if (vty->cp == 0) + return; + + _vty_backward_char(vty); + _vty_delete_char(vty); +} + +/* Delete a word before the point. */ +static void _vty_forward_kill_word(vty_t *vty) +{ + while(vty->cp != vty->length && vty->buf[vty->cp] == ' ') + _vty_delete_char(vty); + while(vty->cp != vty->length && vty->buf[vty->cp] != ' ') + _vty_delete_char(vty); +} + +/* Delete a word before the point. */ +static void _vty_backward_kill_word(vty_t *vty) +{ + while(vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + _vty_delete_backward_char(vty); + while(vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + _vty_delete_backward_char(vty); +} + +/* Ensure length of input buffer. Is buffer is short, double it. */ +static void _vty_buf_len_ensure(vty_t *vty, int length) +{ + if (vty->max <= length) + { + vty->max *= 2; + vty->buf = XREALLOC(MTYPE_VTY, vty->buf, vty->max); + } +} + +/* Basic function to insert character into vty. */ +static void _vty_insert_char(vty_t *vty, char c) +{ + unsigned int i = 0; + unsigned int length = 0; + + _vty_buf_len_ensure(vty, vty->length + 1); + length = vty->length - vty->cp; + memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); + vty->buf[vty->cp] = c; + + if (vty->node != PASSWORD_NODE) + { + _vty_write(vty, &vty->buf[vty->cp], length + 1); + for (i = 0; i < length; i++) + _vty_write(vty, &vty_backward_char, 1); + } + + vty->cp++; + vty->length++; +} + +/* Insert a word into vty interface with overwrite mode. */ +static void _vty_insert_word(vty_t *vty, char *str) +{ + int len = strlen(str); + _vty_write(vty, str, len); + strcpy(&vty->buf[vty->cp], str); + vty->cp += len; + vty->length = vty->cp; +} + +static void _vty_describe_command(vty_t *vty) +{ + array_t *cmd_line = NULL; + + cmd_line = cmd_strs_create(vty->buf); + /* In case of '> ?'. */ + if (NULL == cmd_line) + { + cmd_line = array_init(1, MTYPE_CLI); + array_append(cmd_line, '\0', MTYPE_CLI); + } + else if(vty->length && isspace((int)vty->buf[vty->length - 1])) + array_append(cmd_line, '\0', MTYPE_CLI); + + vty_question(vty, cmd_line); + + cmd_strs_free(cmd_line); + vty_prompt(vty); + _vty_redraw_line(vty); + return; +} + +static void _vty_append_word(vty_t *vty, array_t *cmd_line, char *word, int status) +{ + unsigned int index = array_active(cmd_line) - 1; + + _vty_end_of_line(vty); + + if (NULL == array_get(cmd_line, index)) + _vty_insert_word(vty, word); + else + { + _vty_backward_word(vty); + _vty_insert_word(vty, word); + } + + if (CMD_COMPLETE_FULL_MATCH == status) + _vty_insert_char(vty, ' '); +} + +int _vty_complete_command(vty_t *vty) +{ + array_t *cmd_line = NULL; + char **match_strs = NULL; + int32_t complete_status = CMD_ERR_NO_MATCH; + + cmd_line = cmd_strs_create(vty->buf); + if (NULL == cmd_line) + { + cmd_line = array_init(1, MTYPE_CLI); + array_append(cmd_line, '\0', MTYPE_CLI); + } + else if (vty->length && isspace((int)vty->buf[vty->length - 1])) + array_append(cmd_line, '\0', MTYPE_CLI); + + match_strs = cmd_complete_command(cmd_line, vty, &complete_status); + if (NULL == match_strs) + { + cmd_strs_free(cmd_line); + return 0; + } + + if (CMD_COMPLETE_MATCH == complete_status || + CMD_COMPLETE_FULL_MATCH == complete_status) + _vty_append_word(vty, cmd_line, match_strs[0], complete_status); + else + { + vty_print_word(vty, match_strs); + vty_prompt(vty); + _vty_redraw_line(vty); + } + + vty_free_match_strs(match_strs); + cmd_strs_free(cmd_line); + + return 0; +} + +static int _vty_read(thread_t *thread) +{ + int i = 0; + int nbytes = 0; + unsigned char buf[VTY_READ_BUFSIZ]; + int vty_sock = THREAD_FD(thread); + vty_t *vty = THREAD_ARG(thread); + + vty->t_read = NULL; + + /* Read raw data from socket */ + if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) + { + if (nbytes < 0) + { + if (ERRNO_IO_RETRY(errno)) + { + vty_event(VTY_READ, vty_sock, vty); + return 0; + } + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + printf("%s: read error on vty client fd %d, closing: %s\n", + __func__, vty->fd, safe_strerror(errno)); + } + + buf_reset(vty->out_buf); + vty->status = VTY_CLOSE; + } + + for (i = 0; i < nbytes; i++) + { + if (IAC == buf[i]) + { + if (!vty->iac) + { + vty->iac = 1; + continue; + } + else + { + vty->iac = 0; + } + } + + if (vty->iac_sb_in_progress && !vty->iac) + { + if (vty->sb_len < sizeof(vty->sb_buf)) + vty->sb_buf[vty->sb_len] = buf[i]; + vty->sb_len++; + continue; + } + + if (vty->iac) + { + /* In case of telnet command */ + int ret = 0; + ret = _vty_telnet_option(vty, &buf[i], nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + + if (VTY_MORE == vty->status) + { + switch (buf[i]) + { + case CONTROL('C'): + case 'q': + case 'Q': + _vty_buffer_reset(vty); + break; + default: + break; + } + + continue; + } + + /* Escape character. */ + if (VTY_ESCAPE == vty->escape) + { + _vty_escape_map(buf[i], vty); + continue; + } + + /* Pre-escape status. */ + if (vty->escape == VTY_PRE_ESCAPE) + { + switch (buf[i]) + { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + _vty_backward_word(vty); + vty->escape = VTY_NO_ESCAPE; + break; + case 'f': + _vty_forward_word(vty); + vty->escape = VTY_NO_ESCAPE; + break; + case 'd': + _vty_forward_kill_word(vty); + vty->escape = VTY_NO_ESCAPE; + break; + case CONTROL('H'): + case 0x7f: + _vty_backward_kill_word(vty); + vty->escape = VTY_NO_ESCAPE; + break; + default: + vty->escape = VTY_NO_ESCAPE; + break; + } + + continue; + } + + switch (buf[i]) + { + case CONTROL('A'): + _vty_beginning_of_line(vty); + break; + case CONTROL('B'): + _vty_backward_char(vty); + break; + case CONTROL('C'): + //_vty_stop_input(vty); + break; + case CONTROL('D'): + _vty_delete_char(vty); + break; + case CONTROL('E'): + //_vty_end_of_line(vty); + break; + case CONTROL('F'): + _vty_forward_char(vty); + break; + case CONTROL('H'): + case 0x7f: + _vty_delete_backward_char(vty); + break; + case CONTROL('K'): + _vty_kill_line(vty); + break; + case CONTROL('N'): + _vty_next_line(vty); + break; + case CONTROL('P'): + _vty_previous_line(vty); + break; + case CONTROL('T'): + //_vty_transpose_chars(vty); + break; + case CONTROL('U'): + _vty_kill_line_from_beginning(vty); + break; + case CONTROL('W'): + _vty_backward_kill_word(vty); + break; + case CONTROL('Z'): + //_vty_end_config(vty); + break; + case '\n': + case '\r': + vty_out(vty, "%s", VTY_NEWLINE); + vty_execute(vty); + break; + case '\t': + _vty_complete_command(vty); + break; + case '?': + _vty_describe_command(vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') + { + vty->escape = VTY_ESCAPE; + i++; + } + else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + _vty_insert_char(vty, buf[i]); + break; + } + } + + /* Check status. */ + if (vty->status == VTY_CLOSE) + vty_close(vty); + else + { + vty_event(VTY_WRITE, vty_sock, vty); + vty_event(VTY_READ, vty_sock, vty); + } + + return 0; +} + +static void _vty_thread(void *arg) +{ + thread_t thread; + + /* Fetch next active thread. */ + while(thread_fetch(vty_master, &thread)) + thread_call(&thread); + + printf("vty thread is exit!\n"); + return; +} + +static void _vty_thread_init(void) +{ + int32_t rv = 0; + + pthread_t ppid = 0; + struct sched_param param; + pthread_attr_t attr; + + pthread_attr_init(&attr); + param.sched_priority = 50; + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + + rv = pthread_create(&ppid, &attr, (void *)_vty_thread, NULL); + if (rv != 0) + printf("can't create pthread : %d(%s)\n", rv, safe_strerror(rv)); + + pthread_attr_destroy(&attr); +} + +/* Create new vty structure. */ +static vty_t *_vty_term_create(int vty_sock, SOCKUNION_U *su) +{ + vty_t *vty = NULL; + + vty = vty_create(); + + vty->type = VTY_TERM; + vty->node = USERNAME_NODE; + vty->fd = vty_sock; + vty->address = sockunion_su2str(su); + vty->fail_count = 0; + + vty->cp = 0; + vty->length = 0; + + memset(vty->hist, 0, sizeof(vty->hist)); + vty->hp = 0; + vty->hindex = 0; + + vty->status = VTY_NORMAL; + + if (host.lines >= 0) + vty->lines = host.lines; + else + vty->lines = -1; + + vty->iac = 0; + vty->iac_sb_in_progress = 0; + vty->sb_len = 0; + + vty->v_timeout = VTY_TIMEOUT_VAL; + + array_set(vtyvec, vty_sock, vty, MTYPE_VTY_TMP); + + /* Setting up terminal. */ + vty_will_echo(vty); + vty_will_suppress_go_ahead(vty); + + vty_dont_linemode(vty); + vty_do_window_size(vty); + /* vty_dont_lflow_ahead (vty); */ + + vty_prompt(vty); + + /* Add read/write thread. */ + vty_event(VTY_WRITE, vty_sock, vty); + vty_event(VTY_READ, vty_sock, vty); + + return vty; +} + +/* Accept connection from the network. */ +static int _vty_accept(thread_t *thread) +{ + int vty_sock = 0; + SOCKUNION_U su; + int ret = -1; + unsigned int on = 0; + int accept_sock = 0; + prefix_t *p = NULL; + access_list_t *acl = NULL; + char *bufp = NULL; + + accept_sock = THREAD_FD(thread); + + /* We continue hearing vty socket. */ + vty_event(VTY_SERV, accept_sock, NULL); + + memset(&su, 0, sizeof(SOCKUNION_U)); + + /* We can handle IPv4 or IPv6 socket. */ + vty_sock = sockunion_accept(accept_sock, &su); + if (vty_sock < 0) + { + printf("can't accept vty socket : %s\n", safe_strerror(errno)); + return -1; + } + set_nonblocking(vty_sock); + + p = sockunion2hostprefix(&su); + + /* VTY's accesslist apply. */ + if (AF_INET == p->family && vty_accesslist_name) + { + if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name)) && + (FILTER_DENY == access_list_apply(acl, p))) + { + char *buf = NULL; + printf("Vty connection refused from %s\n", (buf = sockunion_su2str(&su))); + XFREE(MTYPE_PREFIX, buf); + close(vty_sock); + + /* continue accepting connections */ + vty_event(VTY_SERV, accept_sock, NULL); + + prefix_free(p); + return 0; + } + } + +#ifdef HAVE_IPV6 + /* VTY's ipv6 accesslist apply. */ + if (AF_INET6 == p->family && vty_ipv6_accesslist_name) + { + if ((acl = access_list_lookup(AFI_IP6, vty_ipv6_accesslist_name)) + && (FILTER_DENY == access_list_apply(acl, p))) + { + char *buf = NULL; + printf("Vty connection refused from %s\n", (buf = sockunion_su2str(&su))); + XFREE(MTYPE_PREFIX, buf); + close(vty_sock); + + /* continue accepting connections */ + vty_event(VTY_SERV, accept_sock, NULL); + + prefix_free(p); + + return 0; + } + } +#endif /* HAVE_IPV6 */ + + prefix_free(p); + + on = 1; + ret = setsockopt(vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on, sizeof(on)); + if (ret < 0) + printf("can't set sockopt to vty_sock : %s\n", safe_strerror(errno)); + + printf("Vty connection from %s\n", (bufp = sockunion_su2str(&su))); + if (bufp) + XFREE(MTYPE_PREFIX, bufp); + + _vty_term_create(vty_sock, &su); + + return 0; +} + +/* When time out occur output message then close connection. */ +static int _vty_timeout(thread_t *thread) +{ + vty_t *vty = THREAD_ARG(thread); + + vty->t_timeout = NULL; + vty->v_timeout = 0; + + /* Clear buffer*/ + buf_reset(vty->out_buf); + vty_out(vty, "%sVty connection is timed out.%s\n", VTY_NEWLINE, VTY_NEWLINE); + + /* Close connection. */ + vty->status = VTY_CLOSE; + vty_close(vty); + + return 0; +} + +/* Flush buffer to the vty. */ +static int _vty_flush(thread_t *thread) +{ + int32_t erase = 0; + BUF_STATUS_E flushrc = BUFFER_ERROR; + int vty_sock = THREAD_FD(thread); + vty_t *vty = THREAD_ARG(thread); + + vty->t_write = NULL; + + /* Tempolary disable read thread. */ + if ((0 == vty->lines) && vty->t_read) + { + thread_cancel(vty->t_read); + vty->t_read = NULL; + } + + /* Function execution continue. */ + erase = ((VTY_MORE == vty->status || VTY_MORELINE == vty->status)); + + /* N.B. if width is 0, that means we don't know the window size. */ + if ((vty->lines == 0) || (vty->width == 0)) + flushrc = buf_flush_available(vty->out_buf, vty->fd); + else if (VTY_MORELINE == vty->status) + flushrc = buf_flush_window(vty->out_buf, vty->fd, vty->width, 1, erase, 0); + else + flushrc = buf_flush_window(vty->out_buf, vty->fd, vty->width, + vty->lines >= 0 ? vty->lines : vty->height, erase, 0); + + switch (flushrc) + { + case BUFFER_ERROR: + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + printf("buffer_flush failed on vty client fd %d, closing\n", vty->fd); + buf_reset(vty->out_buf); + vty_close(vty); + return 0; + case BUFFER_EMPTY: + if (VTY_CLOSE == vty->status) + vty_close (vty); + else + { + vty->status = VTY_NORMAL; + if (0 == vty->lines) + vty_event(VTY_READ, vty_sock, vty); + } + break; + case BUFFER_PENDING: + /* There is more data waiting to be written. */ + vty->status = VTY_MORE; + if (0 == vty->lines) + vty_event(VTY_WRITE, vty_sock, vty); + break; + } + + return 0; +} + +static int _vty_log_out(vty_t *vty, const char *level, const char *proto_str, const char *format, + char *time_str, va_list va) +{ + int32_t ret = 0; + int32_t len = 0; + char *buf = NULL; + + + buf = XMALLOC(MTYPE_VTY_TMP, VTY_TMP_BUFSIZ); + if (NULL == buf) + return -1; + + len = snprintf(buf, VTY_TMP_BUFSIZ, "%s", time_str); + buf[len++] = ' '; + + if (level) + ret = snprintf(buf + len, VTY_TMP_BUFSIZ - len - 1, "%s: %s: ", level, proto_str); + else + ret = snprintf(buf + len, VTY_TMP_BUFSIZ - len - 1, "%s: ", proto_str); + + len += ret; + if ((ret < 0) || (len > VTY_TMP_BUFSIZ)) + { + XFREE(MTYPE_VTY_TMP, buf); + return -1; + } + + ret = vsnprintf(buf + len, VTY_TMP_BUFSIZ - len - 1, format, va); + len += ret; + if ((ret < 0) || (len > VTY_TMP_BUFSIZ)) + { + XFREE(MTYPE_VTY_TMP, buf); + return -1; + } + + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + buf_flush_all(vty->out_buf, vty->fd); + + XFREE(MTYPE_VTY_TMP, buf); + return 0; +} + +static int32_t _vty_user_cmp(vty_user_t *user) +{ + int32_t i = 0; + vty_user_t *node = NULL; + + for(i = 0; i < array_active(vty_user); i++) + if ((node = array_get(vty_user, i)) != NULL) + { + if (0 == strcmp(user->username, node->username) + && 0 == strcmp(user->password, node->password)) + { + return 0; + } + } + + return 1; +} + +/* 在终端输出版本信息 */ +void vty_version_print(vty_t *vty) +{ + char buf[128] = {0}; + time_t temp = 0; + + vty_out(vty, "%s%s", CL_COPYRIGHT, VTY_NEWLINE); + vty_out(vty, "%s %s (%s).%s", PROGNAME, host.version, host.name ? host.name : "", VTY_NEWLINE); + vty_out(vty, "Compile date: %s%s", host.compile, VTY_NEWLINE); + vty_out(vty, "Id: %04d.%04d%s", device_info.id_major, device_info.id_minor, VTY_NEWLINE); + vty_out(vty, "Mac: %02x:%02x:%02x:%02x:%02x:%02x%s", + device_info.mac[0], device_info.mac[1], device_info.mac[2], + device_info.mac[3], device_info.mac[4], device_info.mac[5], VTY_NEWLINE); + vty_out(vty, "ip: %s %s%s", device_info.host_ip, device_info.host_mask, VTY_NEWLINE); + vty_out(vty, "route: %s%s", device_info.host_gw, VTY_NEWLINE); + temp = device_info.factory_date; + strftime(buf, 128, "%Y-%m-%d %H:%M:%S", localtime(&temp)); + vty_out(vty, "factory date: %s%s", buf, VTY_NEWLINE); + temp = device_info.deployment_date; + strftime(buf, 128, "%Y-%m-%d %H:%M:%S", localtime(&temp)); + vty_out(vty, "deployment date: %s%s", buf, VTY_NEWLINE); +} + +void vty_event(VTY_EVENT_E event, int sock, vty_t *vty) +{ + thread_t *thread = NULL; + + switch (event) + { + case VTY_SERV: + thread = THREAD_ADD_READ(vty_master, _vty_accept, vty, sock); + array_set(vty_serv_thread, sock, thread, MTYPE_VTY_TMP); + break; +#if 0 + case VTYSH_SERV: + thread_add_read(vty_master, vtysh_accept, vty, sock); + break; + case VTYSH_READ: + vty->t_read = THREAD_ADD_READ(vty_master, vtysh_read, vty, sock); + break; + case VTYSH_WRITE: + vty->t_write = THREAD_ADD_WRITE(vty_master, vtysh_write, vty, sock); + break; +#endif /* VTYSH */ + case VTY_READ: + vty->t_read = THREAD_ADD_READ(vty_master, _vty_read, vty, sock); + + /* Time out treatment. */ + if (vty->v_timeout) + { + if (vty->t_timeout) + thread_cancel(vty->t_timeout); + + vty->t_timeout = + THREAD_ADD_TIMER(vty_master, _vty_timeout, vty, vty->v_timeout); + } + break; + case VTY_WRITE: + if (! vty->t_write) + vty->t_write = THREAD_ADD_WRITE(vty_master, _vty_flush, vty, sock); + break; + case VTY_TIMEOUT_RESET: + if (vty->t_timeout) + { + thread_cancel(vty->t_timeout); + vty->t_timeout = NULL; + } + if (vty->v_timeout) + { + vty->t_timeout = + THREAD_ADD_TIMER(vty_master, _vty_timeout, vty, vty->v_timeout); + } + break; + } +} + +static void vty_clear_buf(vty_t *vty) +{ + memset(vty->buf, 0, vty->max); +} + +/* Put out prompt and wait input from user. */ +void vty_prompt(vty_t *vty) +{ + cmd_node_t *node = NULL; + + if (vty->type != VTY_SHELL) + { + node = cmd_node_get(vty->node); + + /* 比如用户密码输入 */ + if (USERNAME_NODE == node->node + || PASSWORD_NODE == node->node) + { + vty_out(vty, "%s", node->prompt); + } + else + { + vty_out(vty, node->prompt, host.name); + } + } +} + +/* Add current command line to the history buffer. */ +static void vty_hist_add(vty_t *vty) +{ + int index; + + if (vty->length == 0) + return; + + index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; + + /* Ignore the same string as previous one. */ + if (vty->hist[index]) + if(0 == strcmp(vty->buf, vty->hist[index])) + { + vty->hp = vty->hindex; + return; + } + + /* Insert history entry. */ + if (vty->hist[vty->hindex]) + XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]); + vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf); + + /* History index rotation. */ + vty->hindex++; + if (vty->hindex == VTY_MAXHIST) + vty->hindex = 0; + + vty->hp = vty->hindex; +} + +int vty_shell(vty_t *vty) +{ + return vty->type == VTY_SHELL ? 1 : 0; +} + +/* VTY standard output function. */ +int vty_out (vty_t *vty, const char *format, ...) +{ + va_list args; + int len = 0; + int size = 1024; + char buf[1024]; + char *p = NULL; + + if (vty_shell(vty)) + { + va_start(args, format); + vprintf(format, args); + va_end(args); + } + else + { + /* Try to write to initial buffer. */ + va_start(args, format); + len = vsnprintf(buf, sizeof buf, format, args); + va_end(args); + + /* Initial buffer is not enough. */ + if (len < 0 || len >= size) + { + while (1) + { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = XREALLOC(MTYPE_VTY_OUT_BUF, p, size); + if (!p) + return -1; + + va_start(args, format); + len = vsnprintf(p, size, format, args); + va_end(args); + + if (len > -1 && len < size) + break; + } + } + + /* When initial buffer is enough to store all output. */ + if (!p) + p = buf; + + /* Pointer p must point out buffer. */ + buf_put(vty->out_buf, p, len); + + /* If p is not different with buf, it is allocated buffer. */ + if (p != buf) + XFREE(MTYPE_VTY_OUT_BUF, p); + } + + return len; +} + +/* Allocate new vty struct. */ +vty_t *vty_create() +{ + vty_t *vty_new = XMALLOC(MTYPE_VTY, sizeof(vty_t)); + + /* Use default buffer size. */ + vty_new->out_buf = buf_create(0); + vty_new->buf = XMALLOC(MTYPE_VTY, VTY_BUFSIZ); + vty_new->max = VTY_BUFSIZ; + + return vty_new; +} + +/* Close vty interface. Warning: call this only from functions that + * will be careful not to access the vty afterwards (since it has + * now been freed). This is safest from top-level functions (called + * directly by the thread dispatcher). */ +void vty_close(vty_t *vty) +{ + int i = 0; + + /* Cancel threads.*/ + if (vty->t_read) + thread_cancel (vty->t_read); + if (vty->t_write) + thread_cancel (vty->t_write); + if (vty->t_timeout) + thread_cancel (vty->t_timeout); + + /* Flush buffer. */ + buf_flush_all(vty->out_buf, vty->fd); + + /* Free input buffer. */ + buf_free(vty->out_buf); + + /* Free command history. */ + for(i = 0; i < VTY_MAXHIST; i++) + if (vty->hist[i]) + XFREE(MTYPE_VTY_HIST, vty->hist[i]); + + /* Unset vector. */ + array_unset(vtyvec, vty->fd); + + /* Close socket. */ + if (vty->fd > 0) + close(vty->fd); + + if (vty->address) + XFREE(MTYPE_PREFIX, vty->address); + if (vty->buf) + XFREE(MTYPE_VTY, vty->buf); + + /* Check configure. */ + vty_config_unlock(vty); + + /* OK free vty. */ + XFREE(MTYPE_VTY, vty); +} + +/* Execute current command line. */ +int vty_execute(vty_t *vty) +{ + int ret = CMD_ERR_NO_MATCH; + + switch (vty->node) + { + case USERNAME_NODE: + snprintf(vty->user.username, VTY_USERNAME_LEN, "%s", vty->buf); + vty->node = PASSWORD_NODE; + break; + + case PASSWORD_NODE: + snprintf(vty->user.password, VTY_USERNAME_LEN, "%s", vty->buf); + if (0 == _vty_user_cmp(&vty->user)) + { + vty->node = ENABLE_NODE; + //vty_out(vty, "%s================================%s", VTY_NEWLINE, VTY_NEWLINE); + //vty_version_print(vty); + //vty_out(vty, "================================%s%s", VTY_NEWLINE, VTY_NEWLINE); + } + else + { + //vty_out(vty, "Username or password is not match!!!%s", VTY_NEWLINE); + vty->node = USERNAME_NODE; + } + break; + + case ENABLE_NODE: + case CONFIG_NODE: + default: + ret = cmd_execute(vty); + if (vty->type != VTY_SHELL) + vty_hist_add(vty); + break; + } + + /* Clear command line buffer. */ + vty->cp = vty->length = 0; + vty_clear_buf(vty); + + //if (vty->status != VTY_CLOSE ) + // vty_prompt(vty); + + return ret; +} + +int vty_config_lock(vty_t *vty) +{ + if (0 == vty_config) + { + vty->config = 1; + vty_config = 1; + } + + return vty->config; +} + +int vty_config_unlock(vty_t *vty) +{ + if (1 == vty_config) + vty_config = 0; + + if (1 == vty->config) + vty->config = 0; + + return vty->config; +} + +void vty_question(vty_t *vty, array_t *cmd_line) +{ + int32_t ret = CMD_ERR_NO_MATCH; + unsigned int i = 0; + array_t *matchs = NULL; + unsigned int width = 0; + desc_t *desc = NULL; + + matchs = cmd_describe_command(cmd_line, vty, &ret); + vty_out(vty, "%s", VTY_NEWLINE); + + /* Ambiguous and no match error. */ + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + fprintf (stdout,"%% Ambiguous command.\n"); + return; + break; + case CMD_ERR_NO_MATCH: + fprintf (stdout,"%% There is no matched command.\n"); + return; + break; + } + + /* Get width of command string. */ + for (i = 0; i < array_active(matchs); i++) + if ((desc = array_get(matchs, i)) != NULL) + { + unsigned int len = 0; + + if (desc->cmd[0] == '\0') + continue; + + len = strlen(desc->cmd); + if (desc->cmd[0] == '.') + len--; + + if (width < len) + width = len; + } + + for (i = 0; i < array_active(matchs); i++) + if ((desc = array_get(matchs, i)) != NULL) + { + if (desc->cmd[0] == '\0') + continue; + + if (!desc->str) + vty_out(vty, " %-s%s", desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); + else + vty_out(vty, " %-*s %s%s", width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); + } + + array_free(matchs, MTYPE_CLI); + return; +} + +void vty_print_word(vty_t *vty, char *strs[]) +{ + char *str = NULL; + unsigned int i = 0; + + vty_out(vty, "%s", VTY_NEWLINE); + for(i = 0; (str = strs[i]) != NULL; i++) + vty_out(vty, "%s", str); +} + +void vty_free_match_strs(char *match_strs[]) +{ + char *str = NULL; + unsigned int i = 0; + + for(i = 0; (str = match_strs[i]) != NULL; i++) + XFREE(MTYPE_CLI, str); + + XFREE(MTYPE_CLI, match_strs); +} + +/* Send WILL TELOPT_ECHO to remote server. */ +void vty_will_echo(vty_t *vty) +{ + unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'}; + vty_out(vty, "%s", cmd); +} + +/* Make suppress Go-Ahead telnet option. */ +void vty_will_suppress_go_ahead(vty_t *vty) +{ + unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'}; + vty_out(vty, "%s", cmd); +} + +/* Make don't use linemode over telnet. */ +void vty_dont_linemode(vty_t *vty) +{ + unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'}; + vty_out(vty, "%s", cmd); +} + +/* Use window size. */ +void vty_do_window_size(vty_t *vty) +{ + unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'}; + vty_out(vty, "%s", cmd); +} + +/* Make vty server socket. */ +void vty_serv_sock_family(const char* addr, unsigned short port, int family) +{ + int32_t ret = 0; + SOCKUNION_U su; + int accept_sock = 0; + void *naddr = NULL; + + memset(&su, 0, sizeof(SOCKUNION_U)); + su.sa.sa_family = family; + if(addr) + switch(family) + { + case AF_INET: + naddr = &su.sin.sin_addr; +#ifdef HAVE_IPV6 + case AF_INET6: + naddr = &su.sin6.sin6_addr; +#endif + } + + if(naddr) + switch(inet_pton(family, addr, naddr)) + { + case -1: + printf("bad address %s\n", addr); + naddr = NULL; + break; + case 0: + printf("error translating address %s: %s\n", addr, safe_strerror(errno)); + naddr = NULL; + } + + /* Make new socket. */ + accept_sock = sockunion_stream_socket(&su); + if (accept_sock < 0) + return; + + /* This is server, so reuse address. */ + sockunion_reuseaddr(accept_sock); + + /* Bind socket to universal address and given port. */ + ret = sockunion_bind(accept_sock, &su, port, naddr); + if (ret < 0) + { + close(accept_sock); /* Avoid sd leak. */ + return; + } + + /* Listen socket under queue 3. */ + ret = listen(accept_sock, 3); + if (ret < 0) + { + printf("can't listen socket\n"); + close(accept_sock); /* Avoid sd leak. */ + return; + } + + /* Add vty server event. */ + vty_event(VTY_SERV, accept_sock, NULL); +} + +/* Small utility function which output log to the VTY. */ +void vty_log(const char *level, const char *proto_str, const char *format, char *time_str, va_list va) +{ + uint32_t i = 0; + vty_t *vty = NULL; + + if (!vtyvec) + return; + + for(i = 0; i < array_active(vtyvec); i++) + if ((vty = array_get(vtyvec, i)) != NULL) + if (vty->monitor) + { + va_list ac; + va_copy(ac, va); + _vty_log_out(vty, level, proto_str, format, time_str, ac); + va_end(ac); + } +} + +/* vty的print函数. */ +void vty_print(const char *format, va_list va) +{ + uint32_t i = 0; + vty_t *vty = NULL; + char *buf = NULL; + + if (!vtyvec) + return; + + for(i = 0; i < array_active(vtyvec); i++) + if ((vty = array_get(vtyvec, i)) != NULL) + { + buf = XMALLOC(MTYPE_VTY_TMP, VTY_TMP_BUFSIZ); + if (NULL == buf) + return; + + vsnprintf(buf, VTY_TMP_BUFSIZ, format, va); + vty_out(vty, "%s", buf); + buf_flush_all(vty->out_buf, vty->fd); + + XFREE(MTYPE_VTY_TMP, buf); + } +} + + +/* Reset all VTY status. */ +void vty_reset(void) +{ + unsigned int i = 0; + vty_t *vty = NULL; + thread_t *serv_thread; + + for(i = 0; i < array_active(vtyvec); i++) + if ((vty = array_get(vtyvec, i)) != NULL) + { + buf_reset(vty->out_buf); + vty->status = VTY_CLOSE; + vty_close(vty); + } + + for (i = 0; i < array_active(vty_serv_thread); i++) + if ((serv_thread = array_get(vty_serv_thread, i)) != NULL) + { + thread_cancel(serv_thread); + array_get(vty_serv_thread, i) = NULL; + close(i); + } + + return; +} + +/* Install vty's own commands like `who' command. */ +void vty_init(void) +{ + vty_user_t *user; + + vtyvec = array_init(ARRAY_MIN_SIZE, MTYPE_VTY_TMP); + + vty_serv_thread = array_init(ARRAY_MIN_SIZE, MTYPE_VTY_TMP); + + vty_user = array_init(ARRAY_MIN_SIZE, MTYPE_VTY_TMP); + user = XMALLOC(MTYPE_VTY_TMP, sizeof(vty_user_t)); + snprintf(user->username, VTY_USERNAME_LEN, "%s", VTY_USERNAME_DEFAULT); + snprintf(user->password, VTY_USERNAME_LEN, "%s", VTY_PASSWORD_DEFAULT); + user->level = 255; + array_append(vty_user, user, MTYPE_VTY_TMP); + + vty_master = thread_master_create(); + + _vty_thread_init(); +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/z_hardware/hwgpio.c b/app/lib/z_hardware/hwgpio.c new file mode 100755 index 0000000..c006d45 --- /dev/null +++ b/app/lib/z_hardware/hwgpio.c @@ -0,0 +1,428 @@ +/****************************************************************************** + * file lib/hardware/hwgpio.c + * author YuLiang + * version 1.0.0 + * date 24-Nov-2021 + * brief This file provides all the gpio operation functions. + * + ****************************************************************************** + * Attention + * + *

© COPYRIGHT(c) 2021 LandPower

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of LandPower nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +/* Includes ------------------------------------------------------------------*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* 标准C库头文件. */ +#include +#include +#include +#include + +/* 用户代码头文件. */ +#include "hwgpio.h" +#include "cmd.h" + +/* Private define ------------------------------------------------------------*/ +/* Private macro -------------------------------------------------------------*/ +/* Private typedef -----------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +static array_t *gpios = NULL; + +int32_t gpio_dau1_pow_idx; +int32_t gpio_dau2_pow_idx; +int32_t gpio_run_idx; +int32_t gpio_sync_idx; +int32_t gpio_err1_idx; + +/* Private function prototypes -----------------------------------------------*/ + +/* Internal functions --------------------------------------------------------*/ +int32_t _gpio_name_get(uint8_t gpio, char *name) +{ + if (!name) + { + return E_NULL; + } + + switch (gpio) + { + case GPIO_RUN: + snprintf(name, DEV_NAME_STR_LEN, "run"); + break; + + case GPIO_SYNC: + snprintf(name, DEV_NAME_STR_LEN, "syn"); + break; + + case GPIO_SYNC_PPS: + snprintf(name, DEV_NAME_STR_LEN, "syn_pps"); + break; + + case GPIO_ERR1: + snprintf(name, DEV_NAME_STR_LEN, "err1"); + break; + + case GPIO_ERR2: + snprintf(name, DEV_NAME_STR_LEN, "err2"); + break; + + case GPIO_ALARM: + snprintf(name, DEV_NAME_STR_LEN, "alarm"); + break; + + case GPIO_DAU1_POW: + snprintf(name, DEV_NAME_STR_LEN, "dau1"); + break; + + case GPIO_DAU2_POW: + snprintf(name, DEV_NAME_STR_LEN, "dau2"); + break; + + case GPIO_CPLD_CS: + snprintf(name, DEV_NAME_STR_LEN, "cpld_cs"); + break; + + case GPIO_CPLD_SCLK: + snprintf(name, DEV_NAME_STR_LEN, "cpld_sclk"); + break; + + case GPIO_CPLD_MOSI: + snprintf(name, DEV_NAME_STR_LEN, "cpld_mosi"); + break; + + case GPIO_CPLD_MISO: + snprintf(name, DEV_NAME_STR_LEN, "cpld_miso"); + break; + + default: + snprintf(name, DEV_NAME_STR_LEN, "default"); + break; + } + + return E_NONE; +} + +CMD(gpio_show, + gpio_show_cmd, + "show gpio", + SHOW_STR + "Gpios\n") +{ + uint16_t i = 0; + char gpio_name[DEV_NAME_STR_LEN] = {0}; + gpio_node_t *gpio_node = NULL; + + vty_out(vty, "GPIO NAME DIR VALUE%s", VTY_NEWLINE); + for(i = 0; i < array_active(gpios); i++) + { + gpio_node = array_lookup(gpios, i); + if (!gpio_node) + { + continue; + } + + if (gpio_node->curr_dir == GPIO_DIR_IN) + { + gpio_val_get(gpio_node->index, &gpio_node->curr_val); + } + + _gpio_name_get(gpio_node->gpio, gpio_name); + + vty_out(vty, "%4d %-12s %-03s %d%s", gpio_node->gpio, gpio_name, gpio_node->curr_dir == GPIO_DIR_IN ? "in" : "out", + gpio_node->curr_val, VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +/* Interface functions -------------------------------------------------------*/ +/* 根据 gpio 找到对应的结构体, 如果不存在, 返回NULL. */ +gpio_node_t *gpio_get_by_gpio(uint16_t gpio) +{ + uint16_t i = 0; + gpio_node_t *gpio_node = NULL; + + for(i = 0; i < array_active(gpios); i++) + { + gpio_node = array_lookup(gpios, i); + if (!gpio_node) + { + continue; + } + + if (gpio_node->gpio == gpio) + { + return gpio_node; + } + } + + return NULL; +} + +/* 根据数组 index 找到对应的结构体, 如果不存在, 返回 NULL. */ +gpio_node_t *gpio_get_by_index(uint16_t index) +{ + return (gpio_node_t*)array_lookup(gpios, index); +} + +/* 设置gpio输出值 */ +int32_t gpio_val_set(uint16_t index, uint8_t value) +{ + int32_t fd = 0; + char buf[40] = {0}; + gpio_node_t *gpio_node = gpio_get_by_index(index); + + /* 参数检查 */ + if (!gpio_node) + { + DBG(DBG_M_GPIO, "gpio index %d is not found\r\n", index); + return E_NOT_FOUND; + } + + /* 只有out方向可以设置值 */ + if (gpio_node->curr_dir != GPIO_DIR_OUT) + { + DBG(DBG_M_GPIO, "gpio %d direction is not out\r\n", gpio_node->gpio); + return E_BAD_PARAM; + } + + /* 如果值相等就不需要再操作了 */ + if (gpio_node->curr_val == value) + { + return E_NONE; + } + + /* 打开文件 */ + snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio_node->gpio); + fd = open(buf, O_WRONLY); + if (fd <= 0) + { + DBG(DBG_M_GPIO, "Open %s error", buf); + return E_SYS_CALL; + } + + /* 设置值 */ + snprintf(buf, sizeof(buf), "%d", (value == 0) ? 0 : 1); + if (write(fd, buf, 1) <= 0) + { + DBG(DBG_M_GPIO, "Write gpio %d value error", gpio_node->gpio); + return E_SYS_CALL; + } + close(fd); + + gpio_node->curr_val = value; + return E_NONE; +} + +/* 设置gpio输出值 */ +int32_t gpio_val_get(uint16_t index, uint8_t *value) +{ + int32_t fd = 0; + char buf[40] = {0}; + gpio_node_t *gpio_node = gpio_get_by_index(index); + + /* 参数检查 */ + if (!gpio_node) + { + DBG(DBG_M_GPIO, "gpio index %d is not found\r\n", index); + return E_NOT_FOUND; + } + + /* out方向直接读取 */ + if (GPIO_DIR_OUT == gpio_node->curr_dir) + { + *value = gpio_node->curr_val; + return E_NONE; + } + + /* 打开文件 */ + snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio_node->gpio); + fd = open(buf, O_RDONLY); + if (fd <= 0) + { + DBG(DBG_M_GPIO, "Open %s error", buf); + return E_SYS_CALL; + } + + /* 读取值 */ + memset(buf, 0, sizeof(buf)); + if (read(fd, buf, sizeof(buf) - 1) <= 0) + { + DBG(DBG_M_GPIO, "Read gpio %d value error", gpio_node->gpio); + return E_SYS_CALL; + } + close(fd); + + *value = strtol(buf, NULL, 10); + return E_NONE; +} + +/* 设置gpio方向 */ +int32_t gpio_dir_set(uint16_t index, uint8_t dir) +{ + int32_t fd = 0; + char buf[40] = {0}; + gpio_node_t *gpio_node = gpio_get_by_index(index); + + /* 参数检查 */ + if (!gpio_node) + { + DBG(DBG_M_GPIO, "gpio index %d is not found\r\n", index); + return E_NOT_FOUND; + } + + /* 如果方向相等,不用继续操作 */ + if (gpio_node->curr_dir == dir) + { + return E_NONE; + } + + /* 打开文件 */ + snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio_node->gpio); + fd = open(buf, O_WRONLY); + if (fd <= 0) + { + DBG(DBG_M_GPIO, "Open %s error", buf); + return E_SYS_CALL; + } + + /* 设置方向 */ + snprintf(buf, sizeof(buf), "%s", (dir == GPIO_DIR_IN) ? "in" : "out"); + if (write(fd, buf, strlen(buf)) <= 0) + { + DBG(DBG_M_GPIO, "Write gpio %d direction error", gpio_node->gpio); + return E_SYS_CALL; + } + close(fd); + + gpio_node->curr_dir = dir; + return E_NONE; +} + +/* 导出 GPIO, 返回值大于等于 0, 表示加入数组的 index; 小于 0 表示失败. */ +int32_t gpio_export(uint16_t gpio) +{ + int32_t fd = 0; + char buf[40] = {0}; + gpio_node_t *gpio_node = gpio_get_by_gpio(gpio); + + /* 如果找到表示已经 export. */ + if (gpio_node) + { + return -1; + } + + /* 文件不存在则导出, 存在不做操作. */ + snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d", gpio); + if (access(buf, 0) < 0) + { + /* 打开文件描述符 */ + fd = open("/sys/class/gpio/export", O_WRONLY); + if (fd <= 0) + { + log_err(LOG_GPIO, "Open /sys/class/gpio/export error!"); + return -2; + } + + /* 导出gpio */ + snprintf(buf, sizeof(buf), "%d", gpio); + if (write(fd, buf, strlen(buf)) <= 0) + { + log_err(LOG_GPIO, "Write /sys/class/gpio/export(%d) error!", gpio); + return -3; + } + close(fd); + } + + /* 添加结构体 */ + gpio_node = XMALLOC_Q(MTYPE_GPIO, sizeof(gpio_node_t)); + gpio_node->gpio = gpio; + /* 默认值是不确定的 */ + gpio_node->curr_val = 0xff; + gpio_node->is_export = TRUE; + gpio_node->index = array_append(gpios, gpio_node, MTYPE_GPIO); + + return gpio_node->index; +} + +/* 初始化函数 */ +int32_t gpio_init(void) +{ +#if 0 + gpios = array_init(ARRAY_MIN_SIZE, MTYPE_GPIO); +#endif + + cmd_install_element(COMMON_NODE, &gpio_show_cmd); + +#if 0 + gpio_run_idx = gpio_export(GPIO_RUN); + if (gpio_run_idx < 0) + { + DBG(DBG_M_GPIO, "ERROR return %d!\r\n", gpio_run_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_GPIO, gpio_dir_set(gpio_run_idx, GPIO_DIR_OUT)); + + gpio_sync_idx = gpio_export(GPIO_SYNC); + if (gpio_sync_idx < 0) + { + DBG(DBG_M_GPIO, "ERROR return %d!\r\n", gpio_sync_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_GPIO, gpio_dir_set(gpio_sync_idx, GPIO_DIR_OUT)); + + gpio_dau1_pow_idx = gpio_export(GPIO_DAU1_POW); + if (gpio_dau1_pow_idx < 0) + { + DBG(DBG_M_GPIO, "ERROR return %d!\r\n", gpio_dau1_pow_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_GPIO, gpio_dir_set(gpio_dau1_pow_idx, GPIO_DIR_OUT)); + + gpio_dau2_pow_idx = gpio_export(GPIO_DAU2_POW); + if (gpio_dau2_pow_idx < 0) + { + DBG(DBG_M_GPIO, "ERROR return %d!\r\n", gpio_dau2_pow_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_GPIO, gpio_dir_set(gpio_dau2_pow_idx, GPIO_DIR_OUT)); + + gpio_err1_idx = gpio_export(GPIO_ERR2); + if (gpio_err1_idx < 0) + { + DBG(DBG_M_GPIO, "ERROR return %d!\r\n", gpio_err1_idx); + return E_BAD_PARAM; + } + LD_E_RETURN(DBG_M_GPIO, gpio_dir_set(gpio_err1_idx, GPIO_DIR_OUT)); + +#endif + return E_NONE; +} +/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/ diff --git a/app/lib/z_hardware/module.mk b/app/lib/z_hardware/module.mk new file mode 100644 index 0000000..eddec1b --- /dev/null +++ b/app/lib/z_hardware/module.mk @@ -0,0 +1,3 @@ +local_src := $(patsubst $(SOURCE_DIR)/%,%,$(wildcard $(SOURCE_DIR)/$(subdirectory)/*.c)) + +$(eval $(call make-library,$(subdirectory)/libz_hardware.a,$(local_src))) \ No newline at end of file diff --git a/build/Makefile b/build/Makefile new file mode 100755 index 0000000..15fad0b --- /dev/null +++ b/build/Makefile @@ -0,0 +1,131 @@ +# $(call source-to-object,source-file-list($1)) +source-to-object = $(subst .c,.o,$(filter %.c,$1)) + +# 产生库文件相对路径,被每个库程序的module.mk文件使用. +# $(subdirectory) +subdirectory = $(patsubst $(SOURCE_DIR)/%/module.mk,%, \ + $(word \ + $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))) + +# 产生库文件规则,被每个库程序的module.mk文件使用. +$(call make-library,library-name($1),source-file-list($2)) +define make-library + libraries += $1 + sources += $2 + + $1: $(call source-to-object,$2) + $(QUIET)$(AR) $(ARFLAGS) $$@ $$^ $(ENULL) + @echo "$$(INFO_C)AR $$@ done";echo +endef + +# 产生依赖文件. +# $(call make-depend,source-file($1),object-file($2),depend-file($3)) +define make-depend + $(CC) -MM -MF $3 -MP -MT $2 $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) $1 +endef + +SOURCE_DIR := ../app +PRODUCT := $(MAKECMDGOALS) +CONFIG_FILE := $(SOURCE_DIR)/include/config.h +VERSION_FILE := $(SOURCE_DIR)/include/version.h +VERSION_LIB := version.a +DATE_STRING := `date "+%Y.%m.%d %k:%M:%S"` +VERSION_STRING := "1.2" + +MV := mv -f +RM := rm -rf +SED := sed +TEST := test +MKDIR := mkdir -p + +# 交叉编译设置 +#DEFARCH = PC +DEFARCH = MYiR + +ifeq ($(DEFARCH), PC) + CROSS_COMPILE = + CC = $(CROSS_COMPILE)gcc + AR = $(CROSS_COMPILE)ar + CFLAGS = -g -Wall -funwind-tables -rdynamic -DHAVE_CONFIG_H +else ifeq ($(DEFARCH), MYiR) + CFLAGS += -g -Wall -funwind-tables -rdynamic -DHAVE_CONFIG_H +endif + +LDLIB := -lreadline -lncurses -pthread -lrt -lsqlite3 + +# 这里如果是‘@’则隐藏具体的编译命令 +QUIET := @ +ENULL := > /dev/null +INFO_C := "\\33[32mInfo\\33[0m " +ERROR_C := "\\33[31mInfo\\33[0m " + +modules := $(patsubst $(SOURCE_DIR)/%/module.mk,%, \ + $(shell find $(SOURCE_DIR) -name module.mk)) + +# 该变量仅用于产生输出目录,不做任何其他使用. +create-output-directories := $(shell for f in $(modules); \ + do \ + $(TEST) -d $$f || $(MKDIR) $$f; \ + done) +programs := +sources := +libraries := + +objects = $(call source-to-object,$(sources)) +dependencies = $(subst .o,.d,$(objects)) + +include_dirs := $(SOURCE_DIR)/include +CPPFLAGS += $(addprefix -I ,$(include_dirs)) +vpath %.h $(include_dirs) +vpath %.c $(SOURCE_DIR) + +.PHONY: empty +empty: + @echo "Please explicitly specify the Makefile target!!!Example \"make PDMonitor\"." + +include $(patsubst %,$(SOURCE_DIR)/%/module.mk,$(modules)) + +$(VERSION_LIB): $(CONFIG_FILE) $(libraries) version.c + $(QUIET)$(RM) $(VERSION_FILE) + @echo "/* WARNING: Don't modify this file anywhere!!! */" >> $(VERSION_FILE) + @echo "#ifndef _VERSION_H_" >> $(VERSION_FILE) + @echo "#define _VERSION_H_" >> $(VERSION_FILE) + @echo "" >> $(VERSION_FILE) + + @echo "#define VERSION \"$(VERSION_STRING)\"" >> $(VERSION_FILE) + @echo "#define DATE \"$(DATE_STRING)\"" >> $(VERSION_FILE) + + @echo "" >> $(VERSION_FILE) + @echo "char* version_get();" >> $(VERSION_FILE) + @echo "char* version_date_get();" >> $(VERSION_FILE) + + @echo "" >> $(VERSION_FILE) + @echo "#endif" >> $(VERSION_FILE) + @echo "/* WARNING: Don't modify this file anywhere!!! */" >> $(VERSION_FILE) + @echo "COMPILE version.o" + $(QUIET)$(COMPILE.c) -o version.o version.c + $(QUIET)$(AR) $(ARFLAGS) $(VERSION_LIB) version.o $(ENULL) + @echo "$(INFO_C)AR $@ done";echo + +sshcmd: $(CONFIG_FILE) $(libraries) $(VERSION_LIB) + # 使用两次$(libraries)避免库文件之间的交叉引用问题. + $(QUIET)$(LINK.o) -rdynamic $(libraries) $(VERSION_LIB) $(LDLIB) -o $@ + @echo "$(INFO_C)LINK $@ done";echo + # $(QUIET)cp $@ //home//embed//LandPower// + +.PHONY: libraries +libraries: $(libraries) + +.PHONY: clean +clean: + $(RM) $(modules) $(CONFIG_FILE) $(VERSION_FILE) sshcmd $(VERSION_LIB) version.o + +ifneq "$(MAKECMDGOALS)" "clean" + -include $(dependencies) + include config.mk +endif + +%.o: %.c + @echo "COMPILE $@" + $(QUIET)$(call make-depend,$<,$@,$(subst .o,.d,$@)) + $(QUIET)$(COMPILE.c) -o $@ $< diff --git a/build/config.mk b/build/config.mk new file mode 100644 index 0000000..aaa7eb0 --- /dev/null +++ b/build/config.mk @@ -0,0 +1,40 @@ +include $(PRODUCT).cfg + +$(CONFIG_FILE): $(PRODUCT).cfg + $(QUIET)rm $(CONFIG_FILE) -rf + @echo "/* WARNING: Don't modify this file anywhere!!! */" >> $(CONFIG_FILE) + @echo "#ifndef _CONFIG_H_" >> $(CONFIG_FILE) + @echo "#define _CONFIG_H_" >> $(CONFIG_FILE) + @echo "" >> $(CONFIG_FILE) + + @echo "#define PROGNAME \"$(PRODUCT)\"" >> $(CONFIG_FILE) + +# debug开关 +ifeq ($(CFG_DBG_ON), y) + @echo "#define CFG_DBG_ON" >> $(CONFIG_FILE) +endif + +# 设备类型定义 +ifeq ($(CFG_DEV_TYPE_LAND_PD), y) + @echo "#define CFG_DEV_TYPE_LAND_PD" >> $(CONFIG_FILE) +endif + +# 朗德GIS协议开关 +ifeq ($(CFG_PROTO_GIS), y) + @echo "#define CFG_PROTO_GIS" >> $(CONFIG_FILE) +endif + +# 默认使能朗德GIS协议开关 +ifeq ($(CFG_PROTO_GIS_DEFAULT), y) + @echo "#define CFG_PROTO_GIS_DEFAULT" >> $(CONFIG_FILE) +endif + + @echo "" >> $(CONFIG_FILE) + @echo "#include \"common.h\"" >> $(CONFIG_FILE) + + @echo "" >> $(CONFIG_FILE) + @echo "#endif" >> $(CONFIG_FILE) + @echo "/* WARNING: Don't modify this file anywhere!!! */" >> $(CONFIG_FILE) + @echo "$(INFO_C)Create $@ done";echo + +$(VERSION_FILE): $(PRODUCT).cfg \ No newline at end of file diff --git a/build/login_auto.c b/build/login_auto.c new file mode 100644 index 0000000..6a0157a --- /dev/null +++ b/build/login_auto.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + execlp("login", "login", "-f", "root", NULL); + return 0; +} diff --git a/build/sshcmd.cfg b/build/sshcmd.cfg new file mode 100644 index 0000000..5713455 --- /dev/null +++ b/build/sshcmd.cfg @@ -0,0 +1,8 @@ +# debug功能开启 +CFG_DBG_ON := y +# GIS设备 +CFG_DEV_TYPE_LAND_PD := y +# 是否有GIS通讯协议 +CFG_PROTO_GIS := y +# 默认开启GIS通讯协议 +CFG_PROTO_GIS_DEFAULT := y \ No newline at end of file diff --git a/build/version.c b/build/version.c new file mode 100644 index 0000000..cbf4710 --- /dev/null +++ b/build/version.c @@ -0,0 +1,12 @@ +#include "version.h" + +char* version_get() +{ + return VERSION; +} + +char* version_date_get() +{ + return DATE; +} +