commit 9809fe4350bcf198230089b1b89c52f6500fae4e Author: ethan.du Date: Fri Jan 22 16:21:57 2021 +0800 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..8d35ef9 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +## 编译 +编译依赖:cmake 2.8(依赖的nats库仅支持cmake编译) +``` +sudo apt-get install cmake +``` +编译方式:GNU Make +``` +make +``` + +## 打包方式 +将samples改名为main,和依赖的动态链接库(如果有)一起打成zip的压缩包,将压缩包上传到子设备驱动 +``` +cd samples +cp samples main +zip -r abc.zip main +``` + +## API参考文档 + +/** + * @brief 应用初始化 + * + * @param void: + * + * @retval : 成功则返回APP_OK + */ + +app_status app_common_init(void) + +/** + * @brief 获取应用名称 + * + * @param void: + * + * @retval : 成功则返回应用名称的字符串,失败返回NULL + */ + +char *app_get_name(void) + +/** + * @brief 获取产品SN号的字符串 + * + * @param void: + * + * @retval : 成功则返回产品SN号的字符串,失败返回NULL + */ + +char *app_get_productSN(void) + +/** + * @brief 获取设备SN号的字符串 + * + * @param void: + * + * @retval : 成功则返回设备SN号的字符串,失败返回NULL + */ + +char *app_get_deviceSN(void) + +/** + * @brief 获取应用配置信息的字符串 + * + * @param void: + * + * @retval : 成功则返回应用配置信息的字符串,失败返回NULL + */ + +char *app_get_info(void) + +/** + * @brief 注册下行消息回调函数 + * + * @param handle: 下行消息回调函数指针 + * + * @retval : 成功则返回APP_OK + */ + +app_status app_register_cb(msg_handler handle) + +/** + * @brief 记录日志 + * + * @param level: 日志等级分为:DEBUG、INFO、WARNING、ERROR + * @param format: 日志记录格式 + * + * @retval : void + */ + +void log_write(log_level level, const char *format,...) + diff --git a/app/app.c b/app/app.c new file mode 100644 index 0000000..139f1a8 --- /dev/null +++ b/app/app.c @@ -0,0 +1,396 @@ +#include "app.h" + +natsConnection *conn = NULL; +natsSubscription *sub = NULL; +static cJSON *app_config_json = NULL; +char *app_name = NULL; +char *app_productSN = NULL; +char *app_deviceSN = NULL; +char *app_info = NULL; +msg_handler cb = NULL; +char edge_router_subject[NATS_SUBJECT_MAX_LEN] = {0}; + +app_status nats_subscribe(const char *subject, natsMsgHandler cb, void *cbClosure) +{ + app_status status = APP_OK; + + natsConnection_Subscribe(&sub, conn, subject, cb, cbClosure); + natsConnection_Flush(conn); + return status; +} + +app_status nats_publish(const char *topic, const char *str) +{ + app_status status = APP_OK; + + natsConnection_PublishString(conn, topic, str); + natsConnection_Flush(conn); + + return status; +} + +void log_print(const char *format,...) +{ + va_list args; + + va_start(args, format); + vprintf(format, args); + va_end(args); + + fflush(stdout); + return; +} + +void log_write(log_level level, const char *format,...) +{ + if((level < LOG_DEBUG) || (level > LOG_ERROR)) + { + return; + } + + const char *log_lev[4] = {"debug", "info", "warn", "error"}; + struct timeval stamp; + char *msg_str_rep = (char *)APP_MALLOC(NATS_MSG_MAX_LEN); + if(NULL == msg_str_rep) + { + return; + } + memset(msg_str_rep, 0, NATS_MSG_MAX_LEN); + + gettimeofday(&stamp, NULL); + char *msg_str = (char *)APP_MALLOC(NATS_MSG_MAX_LEN); + if(NULL == msg_str) + { + APP_FREE(msg_str_rep); + return; + } + memset(msg_str, 0, NATS_MSG_MAX_LEN); + + char *log_msg = (char *)APP_MALLOC(NATS_MSG_MAX_LEN); + if(NULL == log_msg) + { + APP_FREE(msg_str_rep); + APP_FREE(msg_str); + return; + } + memset(log_msg, 0, NATS_MSG_MAX_LEN); + va_list args; + + va_start(args, format); + vsnprintf(msg_str, NATS_MSG_MAX_LEN, format, args); + va_end(args); + + // 将json字段转换成字符串 + replace_str(msg_str_rep, msg_str, "\"", "\\\""); + snprintf(log_msg, NATS_MSG_MAX_LEN, LOG_UPLOAD_FORMAT, app_get_name(), log_lev[level], msg_str_rep, stamp.tv_sec); + + nats_publish(EDGE_LOG_UPLOAD_SUBJECT, log_msg); + + APP_FREE(msg_str_rep); + APP_FREE(msg_str); + APP_FREE(log_msg); + return; +} + + +static void _parse_config(void) +{ + if(NULL == app_config_json) + { + unsigned int file_len = 0; + FILE *fp; + + file_len = calc_file_len(CONFIG_FILE_PATH); + char *driver_config_str_tmp = (char *)APP_MALLOC(file_len + 1); + if(NULL == driver_config_str_tmp) + { + log_write(LOG_ERROR, "file_str malloc fail!"); + return; + } + memset(driver_config_str_tmp, 0, file_len + 1); + + fp = fopen(CONFIG_FILE_PATH, "r"); + if(NULL == fp) + { + log_write(LOG_ERROR, "cannot open file:%s", CONFIG_FILE_PATH); + return; + } + if(file_len != fread(driver_config_str_tmp, 1, file_len, fp)) + { + log_write(LOG_ERROR, "fread file:%s fail!", CONFIG_FILE_PATH); + } + driver_config_str_tmp[file_len] = '\0'; + fclose(fp); + + log_print("driver_config_str_tmp:%s",driver_config_str_tmp); + app_config_json = cJSON_Parse(driver_config_str_tmp); + APP_FREE(driver_config_str_tmp); + + /* parse appName */ + cJSON *app_name_json = cJSON_GetObjectItem(app_config_json, "appName"); + if(NULL == app_name_json) + { + log_write(LOG_ERROR, "parse appName fail!"); + return; + } + int app_name_len = strlen(app_name_json->valuestring); + app_name = (char *)APP_MALLOC(app_name_len+1); + strncpy(app_name, app_name_json->valuestring, app_name_len); + app_name[app_name_len]='\0'; + + + /* parse productSN */ + cJSON *productSN_json = cJSON_GetObjectItem(app_config_json, "productSN"); + if(NULL == productSN_json) + { + log_write(LOG_ERROR, "parse productSN fail!"); + return; + } + int app_productSN_len = strlen(productSN_json->valuestring); + app_productSN = (char *)APP_MALLOC(app_productSN_len+1); + strncpy(app_productSN, productSN_json->valuestring, app_productSN_len); + app_productSN[app_productSN_len]='\0'; + + /* parse deviceSN */ + cJSON *deviceSN_json = cJSON_GetObjectItem(app_config_json, "deviceSN"); + if(NULL == deviceSN_json) + { + log_write(LOG_ERROR, "parse deviceSN fail!"); + return; + } + int app_deviceSN_len = strlen(deviceSN_json->valuestring); + app_deviceSN = (char *)APP_MALLOC(app_deviceSN_len+1); + strncpy(app_deviceSN, deviceSN_json->valuestring, app_deviceSN_len); + app_deviceSN[app_deviceSN_len]='\0'; + + /* parse appInfo */ + cJSON *appInfo_json = cJSON_GetObjectItem(app_config_json, "appInfo"); + if(NULL == appInfo_json) + { + log_write(LOG_ERROR, "parse appInfo fail!"); + return; + } + int app_info_len = strlen(cJSON_PrintUnformatted(appInfo_json)); + app_info = (char *)APP_MALLOC(app_info_len+1); + strncpy(app_info, cJSON_PrintUnformatted(appInfo_json), app_info_len); + app_info[app_info_len]='\0'; + } + return; +} + +char *app_get_name() +{ + if(NULL == app_name) + return NULL; + return app_name; +} + +char *app_get_productSN() +{ + if(NULL == app_productSN) + return NULL; + return app_productSN; +} + +char *app_get_deviceSN() +{ + if(NULL == app_deviceSN) + return NULL; + return app_deviceSN; +} + +char *app_get_info() +{ + if(NULL == app_info) + return NULL; + return app_info; +} + +static void _handle_message(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure) +{ + log_write(LOG_DEBUG, "Received msg: %s - %.*s", natsMsg_GetSubject(msg), natsMsg_GetDataLength(msg), natsMsg_GetData(msg)); + + cJSON *msg_json = cJSON_Parse(natsMsg_GetData(msg)); + if (!msg_json) + { + log_write(LOG_ERROR, "_handle_message json parse error: [%s]",cJSON_GetErrorPtr()); + goto end; + } + cJSON *topic = cJSON_GetObjectItem(msg_json, "topic"); + if(NULL == topic) + { + log_write(LOG_ERROR, "cannot find topic, illegal msg"); + cJSON_Delete(msg_json); + goto end; + } + log_write(LOG_DEBUG, "_handle_message topic:%s", topic->valuestring); + + cJSON *msg_base64code = cJSON_GetObjectItem(msg_json, "payload"); + if(NULL == msg_base64code) + { + log_write(LOG_ERROR, "cannot find payload, illegal msg"); + cJSON_Delete(msg_json); + goto end; + } + log_write(LOG_DEBUG, "_handle_message msg_base64code:%s", msg_base64code->valuestring); + char *msg_base64decode = (char *)APP_MALLOC(NATS_MSG_MAX_LEN); + if(NULL == msg_base64decode) + { + log_write(LOG_ERROR, "msg_base64decode malloc fail!"); + cJSON_Delete(msg_json); + goto end; + } + memset(msg_base64decode, 0, NATS_MSG_MAX_LEN); + base64_decode(msg_base64code->valuestring, strlen(msg_base64code->valuestring), msg_base64decode); + log_write(LOG_DEBUG, "_handle_message msg_base64decode:%s", msg_base64decode); + + if(NULL != cb) + cb(topic->valuestring, msg_base64decode); + + APP_FREE(msg_base64decode); +end: + natsMsg_Destroy(msg); + cJSON_Delete(msg_json); + return; +} + + +app_status app_register_cb(msg_handler handle) +{ + app_status status = APP_OK; + char edge_app_subject[NATS_SUBJECT_MAX_LEN] = {0}; + + if((NULL == cb) && (NULL != handle)) + cb = handle; + snprintf(edge_app_subject, NATS_SUBJECT_MAX_LEN, EDGE_APP_SUBJECT_FORMAT, app_get_name()); + log_write(LOG_DEBUG, "edge_app_subject:%s",edge_app_subject); + + status = nats_subscribe(edge_app_subject, _handle_message, NULL); + if(APP_OK != status) + { + log_write(LOG_ERROR, "edge_subscribe %s fail! status:%d", edge_app_subject, status); + return APP_PROTOCOL_ERROR; + } + return status; +} + +app_status app_publish(const char *topic, const char *str) +{ + app_status status = APP_OK; + + if((NULL == topic) || (NULL == str)) + { + return APP_INVALID_ARG; + } + + char *normal_payload_base64 = (char *)APP_MALLOC(NATS_MSG_MAX_LEN); + if(NULL == normal_payload_base64) + { + log_write(LOG_ERROR, "normal_payload_base64 malloc fail!"); + return APP_NO_MEMORY; + } + memset(normal_payload_base64, 0, NATS_MSG_MAX_LEN); + base64_encode(str, strlen(str), normal_payload_base64); + log_write(LOG_DEBUG, "dyn_reg_payload_base64:%s",normal_payload_base64); + + char *normal_msg = (char *)APP_MALLOC(NATS_MSG_MAX_LEN); + if(NULL == normal_msg) + { + log_write(LOG_ERROR, "normal_msg malloc fail!"); + APP_FREE(normal_payload_base64); + return APP_NO_MEMORY; + } + memset(normal_msg, 0, NATS_MSG_MAX_LEN); + snprintf(normal_msg, NATS_MSG_MAX_LEN, NORMAL_MSG_FORMAT, topic, normal_payload_base64, app_get_name()); + log_write(LOG_DEBUG, "normal_msg:%s",normal_msg); + + status = nats_publish(edge_router_subject, normal_msg); + + APP_FREE(normal_payload_base64); + APP_FREE(normal_msg); + return status; +} + +static void _handle_status_sync(union sigval v) +{ + app_status status = APP_OK; + char *sync_msg = (char *)APP_MALLOC(NATS_MSG_MAX_LEN); + if(NULL == sync_msg) + { + log_write(LOG_ERROR, "sync_msg malloc fail!"); + return; + } + + memset(sync_msg, 0, NATS_MSG_MAX_LEN); + snprintf(sync_msg, NATS_MSG_MAX_LEN, STATUS_SYNC_FORMAT, app_get_name()); + log_write(LOG_DEBUG, "sync_msg:%s",sync_msg); + status = nats_publish(EDGE_APP_STATUS_SUBJECT, sync_msg); + if(APP_OK != status) + { + log_write(LOG_ERROR, "publish sync msg fail!"); + APP_FREE(sync_msg); + return; + } + + APP_FREE(sync_msg); + return; +} + + +static void _fetch_online_status(void) +{ + struct sigevent evp; + struct itimerspec ts; + timer_t timer; + int ret; + + memset(&evp, 0, sizeof(evp)); + evp.sigev_value.sival_ptr = &timer; + evp.sigev_notify = SIGEV_THREAD; + evp.sigev_notify_function = _handle_status_sync; + evp.sigev_value.sival_int = 0; + + ret = timer_create(CLOCK_REALTIME, &evp, &timer); + if(ret) + perror("timer_create"); + + ts.it_interval.tv_sec = 15; + ts.it_interval.tv_nsec = 0; + ts.it_value.tv_sec = 15; + ts.it_value.tv_nsec = 0; + + ret = timer_settime(timer, 0, &ts, NULL); + if( ret ) + perror("timer_settime"); + return; +} + +app_status app_common_init(void) +{ + app_status status = APP_OK; + char *nats_server_url = NULL; + + // nats server取IOTEDGE_NATS_ADDRESS环境或者本地host + nats_server_url = getenv("IOTEDGE_NATS_ADDRESS"); + status = natsConnection_ConnectTo(&conn, nats_server_url != NULL?nats_server_url:NATS_SERVER_DEFAULT_URL); + if(APP_OK != status) + { + log_print("connect nats fail!\r\n"); + return APP_PROTOCOL_ERROR; + } + + _parse_config(); + + snprintf(edge_router_subject, NATS_SUBJECT_MAX_LEN, EDGE_ROUTER_SUBJECT_FORMAT, app_get_name()); + log_print(LOG_DEBUG, "edge_router_subject:%s",edge_router_subject); + + _fetch_online_status(); + + return APP_OK; +} + + + + + + diff --git a/app/app.h b/app/app.h new file mode 100644 index 0000000..1e3a459 --- /dev/null +++ b/app/app.h @@ -0,0 +1,139 @@ +#ifndef _APP_H +#define _APP_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" +#include "nats.h" +#include "utils.h" + +#define APP_MALLOC(s) malloc((s)) +#define APP_CALLOC(c,s) calloc((c), (s)) +#define APP_REALLOC(p, s) realloc((p), (s)) +#define APP_FREE(p) free((p)) + +typedef enum +{ + LOG_DEBUG = 0, + LOG_INFO, + LOG_WARN, + LOG_ERROR, +}log_level; + +#define CONFIG_FILE_PATH "./etc/iotedge/config.json" + +#define NATS_SERVER_DEFAULT_URL "tcp://127.0.0.1:4222" + +//topic format +#define EDGE_APP_SUBJECT_FORMAT "edge.app.%s" //edge.app.appName +#define EDGE_ROUTER_SUBJECT_FORMAT "edge.router.%s" //edge.router.appName + +#define EDGE_LOG_UPLOAD_SUBJECT "edge.log.upload" //log update +#define EDGE_APP_STATUS_SUBJECT "edge.app.status" + +//payload firmat +#define NORMAL_MSG_FORMAT "{\"src\": \"app\",\"topic\": \"%s\",\"payload\": \"%s\",\"identity\": \"%s\"}" +#define STATUS_SYNC_FORMAT "{\"appName\": \"%s\"}" +#define LOG_UPLOAD_FORMAT "{\"module\": \"application_%s\",\"level\": \"%s\",\"message\": \"%s\",\"timestamp\": %ld}" + +#define NATS_MSG_MAX_LEN 2048 +#define NATS_SUBJECT_MAX_LEN 100 + +typedef enum +{ + APP_OK = 0, ///< Success + + APP_ERR, ///< Generic error + APP_PROTOCOL_ERROR, ///< Error when parsing a protocol message, + /// or not getting the expected message. + APP_IO_ERROR, ///< IO Error (network communication). + APP_LINE_TOO_LONG, ///< The protocol message read from the socket + /// does not fit in the read buffer. + + APP_CONNECTION_CLOSED, ///< Operation on this connection failed because + /// the connection is closed. + APP_NO_SERVER, ///< Unable to connect, the server could not be + /// reached or is not running. + APP_STALE_CONNECTION, ///< The server closed our connection because it + /// did not receive PINGs at the expected interval. + APP_SECURE_CONNECTION_WANTED, ///< The client is configured to use TLS, but the + /// server is not. + APP_SECURE_CONNECTION_REQUIRED, ///< The server expects a TLS connection. + APP_CONNECTION_DISCONNECTED, ///< The connection was disconnected. Depending on + /// the configuration, the connection may reconnect. + + APP_CONNECTION_AUTH_FAILED, ///< The connection failed due to authentication error. + APP_NOT_PERMITTED, ///< The action is not permitted. + APP_NOT_FOUND, ///< An action could not complete because something + /// was not found. So far, this is an internal error. + + APP_ADDRESS_MISSING, ///< Incorrect URL. For instance no host specified in + /// the URL. + + APP_INVALID_SUBJECT, ///< Invalid subject, for instance NULL or empty string. + APP_INVALID_ARG, ///< An invalid argument is passed to a function. For + /// instance passing NULL to an API that does not + /// accept this value. + APP_INVALID_SUBSCRIPTION, ///< The call to a subscription function fails because + /// the subscription has previously been closed. + APP_INVALID_TIMEOUT, ///< Timeout must be positive numbers. + + APP_ILLEGAL_STATE, ///< An unexpected state, for instance calling + /// #natsSubscription_NextMsg() on an asynchronous + /// subscriber. + + APP_SLOW_CONSUMER, ///< The maximum number of messages waiting to be + /// delivered has been reached. Messages are dropped. + + APP_MAX_PAYLOAD, ///< Attempt to send a payload larger than the maximum + /// allowed by the NATS Server. + APP_MAX_DELIVERED_MSGS, ///< Attempt to receive more messages than allowed, for + /// instance because of #natsSubscription_AutoUnsubscribe(). + + APP_INSUFFICIENT_BUFFER, ///< A buffer is not large enough to accommodate the data. + + APP_NO_MEMORY, ///< An operation could not complete because of insufficient + /// memory. + + APP_SYS_ERROR, ///< Some system function returned an error. + + APP_TIMEOUT, ///< An operation timed-out. For instance + /// #natsSubscription_NextMsg(). + + APP_FAILED_TO_INITIALIZE, ///< The library failed to initialize. + APP_NOT_INITIALIZED, ///< The library is not yet initialized. + + APP_SSL_ERROR, ///< An SSL error occurred when trying to establish a + /// connection. + + APP_NO_SERVER_SUPPORT, ///< The server does not support this action. + + APP_NOT_YET_CONNECTED, ///< A connection could not be immediately established and + /// #natsOptions_SetRetryOnFailedConnect() specified + /// a connected callback. The connect is retried asynchronously. + + APP_DRAINING, ///< A connection and/or subscription entered the draining mode. + /// Some operations will fail when in that mode. + + APP_INVALID_QUEUE_NAME, ///< An invalid queue name was passed when creating a queue subscription. + +} app_status; + +typedef void (*msg_handler)(char *topic, char *payload); + +char *app_get_name(); +char *app_get_productSN(); +char *app_get_deviceSN(); +char *app_get_info(); +app_status app_register_cb(msg_handler handle); +app_status app_publish(const char *topic, const char *str); +app_status app_common_init(void); +void log_write(log_level level, const char *format,...); + +#endif diff --git a/app/app.mk b/app/app.mk new file mode 100644 index 0000000..cb54554 --- /dev/null +++ b/app/app.mk @@ -0,0 +1,25 @@ +include ../make.settings +CFLAGS = -g -Wall -O2 -D_GNU_SOURCE -std=c99 + +APP_LIB = libapp.a + +INCLUDE_PATH = -I$(PWD) +INCLUDE = $(INCLUDE_PATH)/build/cjson $(INCLUDE_PATH)/build/nats + +OBJS = ./app.o \ + ./utils.o \ + +all : $(APP_LIB) install + +$(APP_LIB): $(OBJS) + $(AR) cr $@ $(OBJS) + +$(OBJS):%o:%c + $(CC) -c $< -o $@ $(CFLAGS) $(INCLUDE) + +install: + mkdir -p $(PWD)/build/app + cp $(APP_LIB) $(PWD)/build/app + cp *.h $(PWD)/build/app +clean: + -$(RM) -r $(APP_LIB) $(OBJS) diff --git a/app/utils.c b/app/utils.c new file mode 100644 index 0000000..825fd4d --- /dev/null +++ b/app/utils.c @@ -0,0 +1,180 @@ +#include "utils.h" + +// base64 转换表, 共64个 +static const char base64_alphabet[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/'}; + +// 解码时使用 +static const unsigned char base64_suffix_map[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, + 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255 }; + +static char cmove_bits(unsigned char src, unsigned lnum, unsigned rnum) { + src <<= lnum; // src = src << lnum; + src >>= rnum; // src = src >> rnum; + return src; +} + +int base64_encode(const char *indata, int inlen, char *outdata) { + + int ret = 0; // return value + if (indata == NULL || inlen == 0) { + return ret = -1; + } + + int in_len = 0; // 源字符串长度, 如果in_len不是3的倍数, 那么需要补成3的倍数 + int pad_num = 0; // 需要补齐的字符个数, 这样只有2, 1, 0(0的话不需要拼接, ) + if (inlen % 3 != 0) { + pad_num = 3 - inlen % 3; + } + in_len = inlen + pad_num; // 拼接后的长度, 实际编码需要的长度(3的倍数) + + char *p = outdata; // 定义指针指向传出data的首地址 + + //编码, 长度为调整后的长度, 3字节一组 + int i = 0; + for (i = 0; i < in_len; i+=3) { + int value = *indata >> 2; // 将indata第一个字符向右移动2bit(丢弃2bit) + char c = base64_alphabet[value]; // 对应base64转换表的字符 + *p = c; // 将对应字符(编码后字符)赋值给outdata第一字节 + + //处理最后一组(最后3字节)的数据 + if (i == inlen + pad_num - 3 && pad_num != 0) { + if(pad_num == 1) { + *(p + 1) = base64_alphabet[(int)(cmove_bits(*indata, 6, 2) + cmove_bits(*(indata + 1), 0, 4))]; + *(p + 2) = base64_alphabet[(int)cmove_bits(*(indata + 1), 4, 2)]; + *(p + 3) = '='; + } else if (pad_num == 2) { // 编码后的数据要补两个 '=' + *(p + 1) = base64_alphabet[(int)cmove_bits(*indata, 6, 2)]; + *(p + 2) = '='; + *(p + 3) = '='; + } + } else { // 处理正常的3字节的数据 + *(p + 1) = base64_alphabet[cmove_bits(*indata, 6, 2) + cmove_bits(*(indata + 1), 0, 4)]; + *(p + 2) = base64_alphabet[cmove_bits(*(indata + 1), 4, 2) + cmove_bits(*(indata + 2), 0, 6)]; + *(p + 3) = base64_alphabet[*(indata + 2) & 0x3f]; + } + + p += 4; + indata += 3; + } + + return ret; +} + + +int base64_decode(const char *indata, int inlen, char *outdata) { + + int ret = 0; + if (indata == NULL || inlen <= 0 || outdata == NULL) { + return ret = -1; + } + if (inlen % 4 != 0) { // 需要解码的数据不是4字节倍数 + return ret = -2; + } + + int t = 0, x = 0, y = 0, i = 0; + unsigned char c = 0; + int g = 3; + + while (indata[x] != 0) { + // 需要解码的数据对应的ASCII值对应base64_suffix_map的值 + c = base64_suffix_map[(unsigned int)indata[x++]]; + if (c == 255) return -1;// 对应的值不在转码表中 + if (c == 253) continue;// 对应的值是换行或者回车 + if (c == 254) { c = 0; g--; }// 对应的值是'=' + t = (t<<6) | c; // 将其依次放入一个int型中占3字节 + if (++y == 4) { + outdata[i++] = (unsigned char)((t>>16)&0xff); + if (g > 1) outdata[i++] = (unsigned char)((t>>8)&0xff); + if (g > 2) outdata[i++] = (unsigned char)(t&0xff); + y = t = 0; + } + } + return ret; +} + +int calc_file_len(const char *file_path) +{ + FILE *fp; + fp = fopen(file_path, "r"); + if(NULL == fp) + { + printf("cannot open file:%s\r\n",file_path); + return 0; + } + + //计算文件大小,申请内存 + fseek(fp,0L,SEEK_END); + int file_len = ftell(fp); + fclose(fp); + return file_len; +} + +static int _count_string(char *data, char *key) +{ + int count = 0; + int klen = strlen(key); + char *pos_start = data, *pos_end; + while (NULL != (pos_end = strstr(pos_start, key))) { + pos_start = pos_end + klen; + count++; + } + return count; +} + +void replace_str(char *new_buf, char *data, char *rep, char *to) +{ + int rep_len = strlen(rep); + int to_len = strlen(to); + int counts = _count_string(data, rep); + if(0 == counts) + { + strcpy(new_buf, data); + return; + } + + char *pos_start = data, *pos_end, *pbuf = new_buf; + int copy_len; + while (NULL != (pos_end = strstr(pos_start, rep))) { + copy_len = pos_end - pos_start; + strncpy(pbuf, pos_start, copy_len); + pbuf += copy_len; + strcpy(pbuf, to); + pbuf += to_len; + pos_start = pos_end + rep_len; + } + strcpy(pbuf, pos_start); + + return; +} + diff --git a/app/utils.h b/app/utils.h new file mode 100644 index 0000000..224f9f6 --- /dev/null +++ b/app/utils.h @@ -0,0 +1,16 @@ +#ifndef _UTILS_H +#define _UTILS_H +#include +#include +#include +#include +#include +#include +#include +#include + +int base64_encode(const char *indata, int inlen, char *outdata); +int base64_decode(const char *indata, int inlen, char *outdata); +int calc_file_len(const char *file_path); +void replace_str(char *new_buf, char *data, char *rep, char *to); +#endif diff --git a/deps/cJSON-1.7.7.tar.gz b/deps/cJSON-1.7.7.tar.gz new file mode 100644 index 0000000..c2350cf Binary files /dev/null and b/deps/cJSON-1.7.7.tar.gz differ diff --git a/deps/deps.cmake b/deps/deps.cmake new file mode 100644 index 0000000..e9033a0 --- /dev/null +++ b/deps/deps.cmake @@ -0,0 +1,3 @@ +set(CMAKE_SYSTEM_NAME Linux) +#set(CMAKE_C_COMPILER /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc) +set(CMAKE_C_COMPILER gcc) \ No newline at end of file diff --git a/deps/deps.mk b/deps/deps.mk new file mode 100644 index 0000000..4c2e9eb --- /dev/null +++ b/deps/deps.mk @@ -0,0 +1,25 @@ +include ../make.settings +# 解压nats组件源码并完成编译和安装 + +LIB_NATS_TAR = $(PWD)/deps/nats.c-2.1.0 +LIB_CJSON_TAR = $(PWD)/deps/cJSON-1.7.7 +LIB_NATS_DIR = $(PWD)/build/nats +LIB_CJSON_DIR = $(PWD)/build/cjson + +all : + tar xvfz $(LIB_NATS_TAR).tar.gz + mkdir -p $(LIB_NATS_DIR) + cd $(LIB_NATS_TAR) && cmake . -DCMAKE_TOOLCHAIN_FILE=../deps.cmake -DNATS_BUILD_WITH_TLS=OFF -DNATS_BUILD_STREAMING=OFF -DNATS_BUILD_NO_SPIN=ON && $(MAKE) + cp $(LIB_NATS_TAR)/src/libnats_static.a $(LIB_NATS_DIR) + cp $(LIB_NATS_TAR)/src/nats.h $(LIB_NATS_DIR) + cp $(LIB_NATS_TAR)/src/status.h $(LIB_NATS_DIR) + cp $(LIB_NATS_TAR)/src/version.h $(LIB_NATS_DIR) + mkdir -p $(LIB_CJSON_DIR) + tar xvfz $(LIB_CJSON_TAR).tar.gz + cd $(LIB_CJSON_TAR) && $(MAKE) clean && $(MAKE) CC=$(CC) AR=$(AR) static + cp $(LIB_CJSON_TAR)/libcjson.a $(LIB_CJSON_DIR) + cp $(LIB_CJSON_TAR)/cJSON.h $(LIB_CJSON_DIR) + +clean : + rm -rf $(LIB_NATS_TAR) + rm -rf $(LIB_CJSON_TAR) diff --git a/deps/nats.c-2.1.0.tar.gz b/deps/nats.c-2.1.0.tar.gz new file mode 100644 index 0000000..b0f34e3 Binary files /dev/null and b/deps/nats.c-2.1.0.tar.gz differ diff --git a/make.settings b/make.settings new file mode 100644 index 0000000..382827e --- /dev/null +++ b/make.settings @@ -0,0 +1,5 @@ +#PLATFORM_TOOL = arm-linux-gnueabihf- +PLATFORM_TOOL = +CC := $(PLATFORM_TOOL)gcc +AR := $(PLATFORM_TOOL)ar +LD:= $(PLATFORM_TOOL)ld \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..97226c1 --- /dev/null +++ b/makefile @@ -0,0 +1,21 @@ +include make.settings +# default compile output +all : + $(MAKE) app_sdk_c + $(MAKE) -C samples -f samples.mk + +# linkedge device access sdk +app_sdk_c : + $(MAKE) -C deps -f deps.mk + $(MAKE) -C app -f app.mk + +# clean tempory compile resource +clean: + $(MAKE) -C deps -f deps.mk clean + $(MAKE) -C app -f app.mk clean + $(MAKE) -C samples -f samples.mk clean + -$(RM) -r ./build + -$(RM) -r ./deps/cJSON-1.7.7/ + -$(RM) -r ./deps/nats.c-master/ + +.PHONY: deps app samples diff --git a/samples/samples.c b/samples/samples.c new file mode 100644 index 0000000..cb38042 --- /dev/null +++ b/samples/samples.c @@ -0,0 +1,69 @@ +#include "app.h" + +void recvmsg_handler(char *topic, char *payload) +{ + log_write(LOG_INFO, "receive topic:%s",topic); + log_write(LOG_INFO, "receive payload:%s",payload); + return; +} + +int main(int argc, char **argv) +{ + app_status status = APP_OK; + char topic_str[100] = {0}; + struct timeval stamp; + char time_stamp[100] = {0}; + cJSON *app_info = NULL; + //初始化,获取产品、设备SN号信息 + status = app_common_init(); + if(APP_OK != status) + { + log_write(LOG_ERROR, "app_common_init fail"); + goto end; + } + + log_write(LOG_INFO, "productSN:%s, deviceSN:%s",app_get_productSN(),app_get_deviceSN()); + log_write(LOG_INFO, "app info:%s",app_get_info()); + + //注册回调函数 + status = app_register_cb(recvmsg_handler); + if(APP_OK != status) + { + log_write(LOG_ERROR, "app_register_cb fail"); + goto end; + } + + //获取应用配置信息 + app_info = cJSON_Parse(app_get_info()); + if (!app_info) + { + log_write(LOG_ERROR, "parse app info fail"); + goto end; + } + /* + "topic":"/%s/%s/upload" + */ + cJSON *topic_format = cJSON_GetObjectItem(app_info, "topic"); + + snprintf(topic_str, 100, topic_format->valuestring, app_get_productSN(), app_get_deviceSN()); + while(1) + { + sleep(5); + gettimeofday(&stamp, NULL); + memset(time_stamp, 0, 100); + snprintf(time_stamp, 100, "{\"timestamp\": \"%ld\"}", stamp.tv_sec); + log_write(LOG_INFO, "send message[%s]", time_stamp); + + status = app_publish(topic_str, time_stamp); + if(APP_OK != status) + { + log_write(LOG_ERROR, "app_publish fail"); + goto end; + } + } + +end: + cJSON_Delete(app_info); + return status; +} + diff --git a/samples/samples.mk b/samples/samples.mk new file mode 100644 index 0000000..2450747 --- /dev/null +++ b/samples/samples.mk @@ -0,0 +1,16 @@ +include ../make.settings +CFLAGS = -g -Wall -O2 -lpthread + +INCLUDE = -I../build/cjson -I../build/nats -I../build/app +LIBS = ../build/app/libapp.a ../build/nats/libnats_static.a ../build/cjson/libcjson.a + +src = samples.c +target = samples + +all : $(target) + +$(target) : + $(CC) -o $(target) $(src) $(LIBS) $(INCLUDE) $(CFLAGS) -lrt + +clean: + -$(RM) $(target) $(target).o