From f991b0e35ad1bd3b999c70e68c518bae91bd36a6 Mon Sep 17 00:00:00 2001 From: Sylvain Miermont Date: Fri, 28 Mar 2014 16:58:48 +0100 Subject: v1.3.0 - Added TX power management. - Added full support for SX1301 reference board. - Changed build system with configuration for multiple chip/radio/band support. - SX125x bandwidth set to 1MHz by default (was 800 kHz). - Solved warnings with 64b integer printf when compiling on x86_64. - Renamed helper programs to reduce the concentrator vs. gateway confusion. --- loragw_pkt_logger/LICENSE.TXT | 49 --- loragw_pkt_logger/Makefile | 40 -- loragw_pkt_logger/README | 88 ---- loragw_pkt_logger/global_conf.json | 46 --- loragw_pkt_logger/inc/parson.h | 100 ----- loragw_pkt_logger/local_conf.json | 5 - loragw_pkt_logger/obj/.gitkeep | 0 loragw_pkt_logger/src/loragw_pkt_logger.c | 566 -------------------------- loragw_pkt_logger/src/parson.c | 649 ------------------------------ 9 files changed, 1543 deletions(-) delete mode 100644 loragw_pkt_logger/LICENSE.TXT delete mode 100644 loragw_pkt_logger/Makefile delete mode 100644 loragw_pkt_logger/README delete mode 100644 loragw_pkt_logger/global_conf.json delete mode 100644 loragw_pkt_logger/inc/parson.h delete mode 100644 loragw_pkt_logger/local_conf.json delete mode 100644 loragw_pkt_logger/obj/.gitkeep delete mode 100644 loragw_pkt_logger/src/loragw_pkt_logger.c delete mode 100644 loragw_pkt_logger/src/parson.c (limited to 'loragw_pkt_logger') diff --git a/loragw_pkt_logger/LICENSE.TXT b/loragw_pkt_logger/LICENSE.TXT deleted file mode 100644 index 2007c51..0000000 --- a/loragw_pkt_logger/LICENSE.TXT +++ /dev/null @@ -1,49 +0,0 @@ ---- For the parson library (parson.c and parson.h) --- - - Parson ( http://kgabis.github.com/parson/ ) - Copyright (c) 2012 Krzysztof Gabis - - 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. - - --- For the rest of the code : Revised BSD License --- - -Copyright (c) 2013, SEMTECH S.A. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * 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. - * Neither the name of the Semtech corporation 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 SEMTECH S.A. 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. diff --git a/loragw_pkt_logger/Makefile b/loragw_pkt_logger/Makefile deleted file mode 100644 index 8946c5b..0000000 --- a/loragw_pkt_logger/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -### Application-specific constants - -APP_NAME=loragw_pkt_logger - -### constant symbols -CROSS_COMPILE= -CC=gcc -CFLAGS=-O2 -Wall -Wextra -Iinc -C99FLAGS=-O2 -Wall -Wextra -std=c99 -Iinc -FLAG_AUX= - -### constants for Lora Gateway HAL library - -LGW_PATH=../libloragw -LGW_INC=-I$(LGW_PATH)/inc - -LGW_LNK=-lloragw -lrt -lmpsse -#LGW_LNK=-lloragw -lrt -# add libmpsse or not, depending on what option you compiled the libloragw with - -### general build targets - -all: $(APP_NAME) - -clean: - rm -f obj/*.o - rm -f $(APP_NAME) - -### sub-modules compilation - -obj/parson.o: src/parson.c - $(CROSS_COMPILE)$(CC) -c $(C99FLAGS) -o obj/parson.o $(LGW_INC) src/parson.c $(FLAG_AUX) - -### main program compilation and assembly - -obj/$(APP_NAME).o: src/$(APP_NAME).c - $(CROSS_COMPILE)$(CC) -c $(C99FLAGS) -o obj/$(APP_NAME).o $(LGW_INC) src/$(APP_NAME).c $(FLAG_AUX) - -$(APP_NAME): $(LGW_PATH)/libloragw.a obj/$(APP_NAME).o obj/parson.o - $(CROSS_COMPILE)$(CC) -o $(APP_NAME) obj/$(APP_NAME).o obj/parson.o -L$(LGW_PATH) $(LGW_LNK) diff --git a/loragw_pkt_logger/README b/loragw_pkt_logger/README deleted file mode 100644 index 9469904..0000000 --- a/loragw_pkt_logger/README +++ /dev/null @@ -1,88 +0,0 @@ - / _____) _ | | - ( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | - (______/|_____)_|_|_| \__)_____)\____)_| |_| - ©2013 Semtech-Cycleo - -Lora Gateway packet logger -=========================== - -1. Introduction ----------------- - -This software is used to set up a Lora concentrator using a JSON configuration -file and then record all the packets received in a log file, indefinitely, until -the user stops the application. -No filtering is done and all packets that are Lora packets with the correct RF -parameters (frequency, datarate, bandwidth) should appear in the log. - -2. Dependencies ----------------- - -This program uses the Parson library (http://kgabis.github.com/parson/) by -Krzysztof Gabis for JSON parsing. -Many thanks to him for that very practical and well written library. - -This program is a typical example of Lora Gateway HAL usage for receiving -packets. - -Only high-level functions are used (the ones contained in loragw_hal) so there -is no hardware dependencies assuming the HAL is matched with the proper version -of the hardware. -Data structures of the received packets are accessed by name (ie. not at a -binary level) so new functionalities can be added to the API without affecting -that program at all. - -It was tested with v1.0.0 of the libloragw library, and should be compatible -with any later version of the library assuming the API is downward-compatible. - -3. Usage ---------- - -To stop the application, press Ctrl+C. - -The only optional parameter when launching the application is the log rotation -time (in seconds). - -The way the program takes configuration files into account is the following: - * if there is a debug_conf.json parse it, others are ignored - * if there is a global_conf.json parse it, look for the next file - * if there is a local_conf.json parse it -If some parameters are defined in both global and local configuration files, the -local definition overwrites the global definition. - -The global configuration file should be exactly the same throughout your -network, contain all global parameters (parameters for "sensor" radio channels) -and preferably default "safe" values for parameters that are specific for each -gateway (eg. specify a default MAC address). - -The local configuration file should contain parameters that are specific to each -gateway (eg. MAC address, frequency for backhaul radio channels). - -In each configuration file, the program looks for a JSON object named -"SX1301_conf" that should contain the parameters for the Lora concentrator board -(RF channels definition, modem parameters, etc) and another JSON object called -"gateway_conf" that should contain the gateway parameters (gateway MAC address, -IP address of the Lora MAC controller, network authentication parameters, etc). - -To learn more about the JSON configuration format, read the provided JSON files -and the API documentation. A dedicated document will be available later on. - -The received packets are put in a CSV file whose name include the MAC address of -the gateway in hexadecimal format and a UTC timestamp of log starting time in -ISO 8601 recommended compact format: -yyyymmddThhmmssZ (eg. 20131009T172345Z for October 9th, 2013 at 5:23:45PM UTC) - -To able continuous monitoring, the current log file is closed is closed and a -new one is opened every hour (by default, rotation interval is settable by the -user using -r command line option). -No packet is lost during that rotation of log file. -Every log file but the current one can then be modified, uploaded and/or deleted -without any consequence for the program execution. - -4. Changelog -------------- - -2013-10-24, v1 -Initial version. diff --git a/loragw_pkt_logger/global_conf.json b/loragw_pkt_logger/global_conf.json deleted file mode 100644 index 6bc8f06..0000000 --- a/loragw_pkt_logger/global_conf.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "SX1301_conf": { - "radio_0": { - "enable": true, - "freq": 868200000 - }, - "chan_multiSF_0": { - "enable": true, - "radio": 0, - "if": -100000, - "note": "Lora MAC channel, 125kHz, all SF, 868.1 MHz" - }, - "chan_multiSF_1": { - "enable": true, - "radio": 0, - "if": 100000, - "note": "Lora MAC channel, 125kHz, all SF, 868.3 MHz" - }, - "chan_multiSF_2": { - "enable": true, - "radio": 0, - "if": 300000, - "note": "Lora MAC channel, 125kHz, all SF, 868.5 MHz" - }, - "chan_Lora_std": { - "enable": true, - "radio": 0, - "if": 100000, - "bandwidth": 250000, - "spread_factor": 7, - "note": "Lora MAC channel, 250kHz, SF7, 868.3 MHz" - }, - - "chan_FSK": { - "enable": true, - "radio": 0, - "if": 100000, - "bandwidth": 250000, - "datarate": 100000, - "note": "FSK 100kbps channel, 868.3 MHz" - } - }, - "gateway_conf": { - "gateway_ID": "AA555A0000000000" - } -} diff --git a/loragw_pkt_logger/inc/parson.h b/loragw_pkt_logger/inc/parson.h deleted file mode 100644 index 00728b1..0000000 --- a/loragw_pkt_logger/inc/parson.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - Parson ( http://kgabis.github.com/parson/ ) - Copyright (c) 2012 Krzysztof Gabis - - 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 parson_parson_h -#define parson_parson_h - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include /* size_t */ - -/* Types and enums */ -typedef struct json_object_t JSON_Object; -typedef struct json_array_t JSON_Array; -typedef struct json_value_t JSON_Value; - -typedef enum json_value_type { - JSONError = 0, - JSONNull = 1, - JSONString = 2, - JSONNumber = 3, - JSONObject = 4, - JSONArray = 5, - JSONBoolean = 6 -} JSON_Value_Type; - -/* Parses first JSON value in a file, returns NULL in case of error */ -JSON_Value * json_parse_file(const char *filename); - -/* Parses first JSON value in a string, returns NULL in case of error */ -JSON_Value * json_parse_string(const char *string); - -/* JSON Object */ -JSON_Value * json_object_get_value (const JSON_Object *object, const char *name); -const char * json_object_get_string (const JSON_Object *object, const char *name); -JSON_Object * json_object_get_object (const JSON_Object *object, const char *name); -JSON_Array * json_object_get_array (const JSON_Object *object, const char *name); -double json_object_get_number (const JSON_Object *object, const char *name); -int json_object_get_boolean(const JSON_Object *object, const char *name); - -/* dotget functions enable addressing values with dot notation in nested objects, - just like in structs or c++/java/c# objects (e.g. objectA.objectB.value). - Because valid names in JSON can contain dots, some values may be inaccessible - this way. */ -JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name); -const char * json_object_dotget_string (const JSON_Object *object, const char *name); -JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name); -JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name); -double json_object_dotget_number (const JSON_Object *object, const char *name); -int json_object_dotget_boolean(const JSON_Object *object, const char *name); - -/* Functions to get available names */ -size_t json_object_get_count(const JSON_Object *object); -const char * json_object_get_name (const JSON_Object *object, size_t index); - -/* JSON Array */ -JSON_Value * json_array_get_value (const JSON_Array *array, size_t index); -const char * json_array_get_string (const JSON_Array *array, size_t index); -JSON_Object * json_array_get_object (const JSON_Array *array, size_t index); -JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); -double json_array_get_number (const JSON_Array *array, size_t index); -int json_array_get_boolean(const JSON_Array *array, size_t index); -size_t json_array_get_count (const JSON_Array *array); - -/* JSON Value */ -JSON_Value_Type json_value_get_type (const JSON_Value *value); -JSON_Object * json_value_get_object (const JSON_Value *value); -JSON_Array * json_value_get_array (const JSON_Value *value); -const char * json_value_get_string (const JSON_Value *value); -double json_value_get_number (const JSON_Value *value); -int json_value_get_boolean(const JSON_Value *value); -void json_value_free (JSON_Value *value); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/loragw_pkt_logger/local_conf.json b/loragw_pkt_logger/local_conf.json deleted file mode 100644 index 624893d..0000000 --- a/loragw_pkt_logger/local_conf.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "gateway_conf": { - "gateway_ID": "AA555A0000000101" - } -} diff --git a/loragw_pkt_logger/obj/.gitkeep b/loragw_pkt_logger/obj/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/loragw_pkt_logger/src/loragw_pkt_logger.c b/loragw_pkt_logger/src/loragw_pkt_logger.c deleted file mode 100644 index c298878..0000000 --- a/loragw_pkt_logger/src/loragw_pkt_logger.c +++ /dev/null @@ -1,566 +0,0 @@ -/* - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - ©2013 Semtech-Cycleo - -Description: - Configure Lora concentrator and record received packets in a log file - -License: Revised BSD License, see LICENSE.TXT file include in the project -Maintainer: Sylvain Miermont -*/ - - -/* -------------------------------------------------------------------------- */ -/* --- DEPENDANCIES --------------------------------------------------------- */ - -/* fix an issue between POSIX and C99 */ -#if __STDC_VERSION__ >= 199901L - #define _XOPEN_SOURCE 600 -#else - #define _XOPEN_SOURCE 500 -#endif - -#include /* C99 types */ -#include /* bool type */ -#include /* printf fprintf sprintf fopen fputs */ - -#include /* memset */ -#include /* sigaction */ -#include /* time clock_gettime strftime gmtime clock_nanosleep*/ -#include /* getopt access */ -#include /* atoi */ - -#include "parson.h" -#include "loragw_hal.h" - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE MACROS ------------------------------------------------------- */ - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#define MSG(args...) fprintf(stderr,"loragw_pkt_logger: " args) /* message that is destined to the user */ - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE VARIABLES (GLOBAL) ------------------------------------------- */ - -/* signal handling variables */ -struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */ -static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */ -static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */ - -/* configuration variables needed by the application */ -uint64_t lgwm = 0; /* Lora gateway MAC address */ -char lgwm_str[17]; - -/* clock and log file management */ -time_t now_time; -time_t log_start_time; -FILE * log_file = NULL; -char log_file_name[64]; - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE FUNCTIONS DECLARATION ---------------------------------------- */ - -static void sig_handler(int sigio); - -int parse_SX1301_configuration(const char * conf_file); - -int parse_gateway_configuration(const char * conf_file); - -void open_log(void); - -void usage (void); - -/* -------------------------------------------------------------------------- */ -/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */ - -static void sig_handler(int sigio) { - if (sigio == SIGQUIT) { - quit_sig = 1;; - } else if ((sigio == SIGINT) || (sigio == SIGTERM)) { - exit_sig = 1; - } -} - -int parse_SX1301_configuration(const char * conf_file) { - int i; - const char conf_obj[] = "SX1301_conf"; - char param_name[32]; /* used to generate variable parameter names */ - struct lgw_conf_rxrf_s rfconf; - struct lgw_conf_rxif_s ifconf; - JSON_Value *root_val; - JSON_Object *root = NULL; - JSON_Object *conf = NULL; - JSON_Value *val; - uint32_t sf, bw; - - /* try to parse JSON */ - root_val = json_parse_file(conf_file); - root = json_value_get_object(root_val); - if (root == NULL) { - MSG("ERROR: %s id not a valid JSON file\n", conf_file); - exit(EXIT_FAILURE); - } - conf = json_object_get_object(root, conf_obj); - if (conf == NULL) { - MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj); - return -1; - } else { - MSG("INFO: %s does contain a JSON object named %s, parsing SX1301 parameters\n", conf_file, conf_obj); - } - - /* set configuration for RF chains */ - for (i = 0; i < LGW_RF_CHAIN_NB; ++i) { - memset(&rfconf, 0, sizeof(rfconf)); /* initialize configuration structure */ - sprintf(param_name, "radio_%i", i); /* compose parameter path inside JSON structure */ - val = json_object_get_value(conf, param_name); /* fetch value (if possible) */ - if (json_value_get_type(val) != JSONObject) { - MSG("INFO: no configuration for radio %i\n", i); - continue; - } - /* there is an object to configure that radio, let's parse it */ - sprintf(param_name, "radio_%i.enable", i); - val = json_object_dotget_value(conf, param_name); - if (json_value_get_type(val) == JSONBoolean) { - rfconf.enable = (bool)json_value_get_boolean(val); - } else { - rfconf.enable = false; - } - if (rfconf.enable == false) { /* radio disabled, nothing else to parse */ - MSG("INFO: radio %i disabled\n", i); - } else { /* radio enabled, will parse the other parameters */ - sprintf(param_name, "radio_%i.freq", i); - rfconf.freq_hz = (uint32_t)json_object_dotget_number(conf, param_name); - MSG("INFO: radio %i enabled, center frequency %u\n", i, rfconf.freq_hz); - } - /* all parameters parsed, submitting configuration to the HAL */ - if (lgw_rxrf_setconf(i, rfconf) != LGW_HAL_SUCCESS) { - MSG("WARNING: invalid configuration for radio %i\n", i); - } - } - - /* set configuration for Lora multi-SF channels (bandwidth cannot be set) */ - for (i = 0; i < LGW_MULTI_NB; ++i) { - memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */ - sprintf(param_name, "chan_multiSF_%i", i); /* compose parameter path inside JSON structure */ - val = json_object_get_value(conf, param_name); /* fetch value (if possible) */ - if (json_value_get_type(val) != JSONObject) { - MSG("INFO: no configuration for Lora multi-SF channel %i\n", i); - continue; - } - /* there is an object to configure that Lora multi-SF channel, let's parse it */ - sprintf(param_name, "chan_multiSF_%i.enable", i); - val = json_object_dotget_value(conf, param_name); - if (json_value_get_type(val) == JSONBoolean) { - ifconf.enable = (bool)json_value_get_boolean(val); - } else { - ifconf.enable = false; - } - if (ifconf.enable == false) { /* Lora multi-SF channel disabled, nothing else to parse */ - MSG("INFO: Lora multi-SF channel %i disabled\n", i); - } else { /* Lora multi-SF channel enabled, will parse the other parameters */ - sprintf(param_name, "chan_multiSF_%i.radio", i); - ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, param_name); - sprintf(param_name, "chan_multiSF_%i.if", i); - ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, param_name); - // TODO: handle individual SF enabling and disabling (spread_factor) - MSG("INFO: Lora multi-SF channel %i enabled, radio %i selected, IF %i Hz, 125 kHz bandwidth, SF 7 to 12\n", i, ifconf.rf_chain, ifconf.freq_hz); - } - /* all parameters parsed, submitting configuration to the HAL */ - if (lgw_rxif_setconf(i, ifconf) != LGW_HAL_SUCCESS) { - MSG("WARNING: invalid configuration for Lora multi-SF channel %i\n", i); - } - } - - /* set configuration for Lora standard channel */ - memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */ - val = json_object_get_value(conf, "chan_Lora_std"); /* fetch value (if possible) */ - if (json_value_get_type(val) != JSONObject) { - MSG("INFO: no configuration for Lora standard channel\n"); - } else { - val = json_object_dotget_value(conf, "chan_Lora_std.enable"); - if (json_value_get_type(val) == JSONBoolean) { - ifconf.enable = (bool)json_value_get_boolean(val); - } else { - ifconf.enable = false; - } - if (ifconf.enable == false) { - MSG("INFO: Lora standard channel %i disabled\n", i); - } else { - ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.radio"); - ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_Lora_std.if"); - bw = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.bandwidth"); - switch(bw) { - case 500000: ifconf.bandwidth = BW_500KHZ; break; - case 250000: ifconf.bandwidth = BW_250KHZ; break; - case 125000: ifconf.bandwidth = BW_125KHZ; break; - default: ifconf.bandwidth = BW_UNDEFINED; - } - sf = (uint32_t)json_object_dotget_number(conf, "chan_Lora_std.spread_factor"); - switch(sf) { - case 7: ifconf.datarate = DR_LORA_SF7; break; - case 8: ifconf.datarate = DR_LORA_SF8; break; - case 9: ifconf.datarate = DR_LORA_SF9; break; - case 10: ifconf.datarate = DR_LORA_SF10; break; - case 11: ifconf.datarate = DR_LORA_SF11; break; - case 12: ifconf.datarate = DR_LORA_SF12; break; - default: ifconf.datarate = DR_UNDEFINED; - } - MSG("INFO: Lora standard channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, SF %u\n", ifconf.rf_chain, ifconf.freq_hz, bw, sf); - } - if (lgw_rxif_setconf(8, ifconf) != LGW_HAL_SUCCESS) { - MSG("WARNING: invalid configuration for Lora standard channel\n"); - } - } - - /* set configuration for FSK channel */ - memset(&ifconf, 0, sizeof(ifconf)); /* initialize configuration structure */ - val = json_object_get_value(conf, "chan_FSK"); /* fetch value (if possible) */ - if (json_value_get_type(val) != JSONObject) { - MSG("INFO: no configuration for FSK channel\n"); - } else { - val = json_object_dotget_value(conf, "chan_FSK.enable"); - if (json_value_get_type(val) == JSONBoolean) { - ifconf.enable = (bool)json_value_get_boolean(val); - } else { - ifconf.enable = false; - } - if (ifconf.enable == false) { - MSG("INFO: FSK channel %i disabled\n", i); - } else { - ifconf.rf_chain = (uint32_t)json_object_dotget_number(conf, "chan_FSK.radio"); - ifconf.freq_hz = (int32_t)json_object_dotget_number(conf, "chan_FSK.if"); - bw = (uint32_t)json_object_dotget_number(conf, "chan_FSK.bandwidth"); - if (bw <= 7800) ifconf.bandwidth = BW_7K8HZ; - else if (bw <= 15600) ifconf.bandwidth = BW_15K6HZ; - else if (bw <= 31200) ifconf.bandwidth = BW_31K2HZ; - else if (bw <= 62500) ifconf.bandwidth = BW_62K5HZ; - else if (bw <= 125000) ifconf.bandwidth = BW_125KHZ; - else if (bw <= 250000) ifconf.bandwidth = BW_250KHZ; - else if (bw <= 500000) ifconf.bandwidth = BW_500KHZ; - else ifconf.bandwidth = BW_UNDEFINED; - ifconf.datarate = (uint32_t)json_object_dotget_number(conf, "chan_FSK.datarate"); - MSG("INFO: FSK channel enabled, radio %i selected, IF %i Hz, %u Hz bandwidth, %u bps datarate\n", ifconf.rf_chain, ifconf.freq_hz, bw, ifconf.datarate); - } - if (lgw_rxif_setconf(9, ifconf) != LGW_HAL_SUCCESS) { - MSG("WARNING: invalid configuration for FSK channel\n"); - } - } - json_value_free(root_val); - return 0; -} - -int parse_gateway_configuration(const char * conf_file) { - const char conf_obj[] = "gateway_conf"; - JSON_Value *root_val; - JSON_Object *root = NULL; - JSON_Object *conf = NULL; - unsigned long long ull = 0; - - /* try to parse JSON */ - root_val = json_parse_file(conf_file); - root = json_value_get_object(root_val); - if (root == NULL) { - MSG("ERROR: %s id not a valid JSON file\n", conf_file); - exit(EXIT_FAILURE); - } - conf = json_object_get_object(root, conf_obj); - if (conf == NULL) { - MSG("INFO: %s does not contain a JSON object named %s\n", conf_file, conf_obj); - return -1; - } else { - MSG("INFO: %s does contain a JSON object named %s, parsing gateway parameters\n", conf_file, conf_obj); - } - - /* getting network parameters (only those necessary for the packet logger) */ - sscanf(json_object_dotget_string(conf, "gateway_ID"), "%llx", &ull); - lgwm = ull; - MSG("INFO: gateway MAC address is configured to %016llX\n", ull); - - json_value_free(root_val); - return 0; -} - -void open_log(void) { - int i; - char iso_date[20]; - - strftime(iso_date,ARRAY_SIZE(iso_date),"%Y%m%dT%H%M%SZ",gmtime(&now_time)); /* format yyyymmddThhmmssZ */ - log_start_time = now_time; /* keep track of when the log was started, for log rotation */ - - sprintf(log_file_name, "pktlog_%s_%s.csv", lgwm_str, iso_date); - log_file = fopen(log_file_name, "a"); /* create log file, append if file already exist */ - if (log_file == NULL) { - MSG("ERROR: impossible to create log file %s\n", log_file_name); - exit(EXIT_FAILURE); - } - - i = fprintf(log_file, "\"gateway ID\",\"node MAC\",\"UTC timestamp\",\"us count\",\"frequency\",\"RF chain\",\"RX chain\",\"status\",\"size\",\"modulation\",\"bandwidth\",\"datarate\",\"coderate\",\"RSSI\",\"SNR\",\"payload\"\n"); - if (i < 0) { - MSG("ERROR: impossible to write to log file %s\n", log_file_name); - exit(EXIT_FAILURE); - } - - MSG("INFO: Now writing to log file %s\n", log_file_name); - return; -} - -/* describe command line options */ -void usage(void) { - MSG( "Available options:\n"); - MSG( " -h print this help\n"); - MSG( " -r rotate log file every N seconds (-1 disable log rotation)\n"); -} - -/* -------------------------------------------------------------------------- */ -/* --- MAIN FUNCTION -------------------------------------------------------- */ - -int main(int argc, char **argv) -{ - int i, j; /* loop and temporary variables */ - struct timespec sleep_time = {0, 3000000}; /* 3 ms */ - - /* clock and log rotation management */ - int log_rotate_interval = 3600; /* by default, rotation every hour */ - int time_check = 0; /* variable used to limit the number of calls to time() function */ - unsigned long pkt_in_log = 0; /* count the number of packet written in each log file */ - - /* configuration file related */ - const char global_conf_fname[] = "global_conf.json"; /* contain global (typ. network-wide) configuration */ - const char local_conf_fname[] = "local_conf.json"; /* contain node specific configuration, overwrite global parameters for parameters that are defined in both */ - const char debug_conf_fname[] = "debug_conf.json"; /* if present, all other configuration files are ignored */ - - /* allocate memory for packet fetching and processing */ - struct lgw_pkt_rx_s rxpkt[16]; /* array containing up to 16 inbound packets metadata */ - struct lgw_pkt_rx_s *p; /* pointer on a RX packet */ - int nb_pkt; - - /* local timestamp variables until we get accurate GPS time */ - struct timespec fetch_time; - char fetch_timestamp[30]; - struct tm * x; - - /* parse command line options */ - while ((i = getopt (argc, argv, "hr:")) != -1) { - switch (i) { - case 'h': - usage(); - return EXIT_FAILURE; - break; - - case 'r': - log_rotate_interval = atoi(optarg); - if ((log_rotate_interval == 0) || (log_rotate_interval < -1)) { - MSG( "ERROR: Invalid argument for -r option\n"); - return EXIT_FAILURE; - } - break; - - default: - MSG("ERROR: argument parsing use -h option for help\n"); - usage(); - return EXIT_FAILURE; - } - } - - /* configure signal handling */ - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigact.sa_handler = sig_handler; - sigaction(SIGQUIT, &sigact, NULL); - sigaction(SIGINT, &sigact, NULL); - sigaction(SIGTERM, &sigact, NULL); - - /* configuration files management */ - if (access(debug_conf_fname, R_OK) == 0) { - /* if there is a debug conf, parse only the debug conf */ - MSG("INFO: found debug configuration file %s, other configuration files will be ignored\n", debug_conf_fname); - parse_SX1301_configuration(debug_conf_fname); - parse_gateway_configuration(debug_conf_fname); - } else if (access(global_conf_fname, R_OK) == 0) { - /* if there is a global conf, parse it and then try to parse local conf */ - MSG("INFO: found global configuration file %s, trying to parse it\n", global_conf_fname); - parse_SX1301_configuration(global_conf_fname); - parse_gateway_configuration(global_conf_fname); - if (access(local_conf_fname, R_OK) == 0) { - MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname); - parse_SX1301_configuration(local_conf_fname); - parse_gateway_configuration(local_conf_fname); - } - } else if (access(local_conf_fname, R_OK) == 0) { - /* if there is only a local conf, parse it and that's all */ - MSG("INFO: found local configuration file %s, trying to parse it\n", local_conf_fname); - parse_SX1301_configuration(local_conf_fname); - parse_gateway_configuration(local_conf_fname); - } else { - MSG("ERROR: failed to find any configuration file named %s, %s or %s\n", global_conf_fname, local_conf_fname, debug_conf_fname); - return EXIT_FAILURE; - } - - /* starting the concentrator */ - i = lgw_start(); - if (i == LGW_HAL_SUCCESS) { - MSG("INFO: concentrator started, packet can now be received\n"); - } else { - MSG("ERROR: failed to start the concentrator\n"); - return EXIT_FAILURE; - } - - /* transform the MAC address into a string */ - sprintf(lgwm_str, "%016llX", lgwm); - - /* opening log file and writing CSV header*/ - time(&now_time); - open_log(); - - /* main loop */ - while ((quit_sig != 1) && (exit_sig != 1)) { - /* fetch packets */ - nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt); - if (nb_pkt == LGW_HAL_ERROR) { - MSG("ERROR: failed packet fetch, exiting\n"); - return EXIT_FAILURE; - } else if (nb_pkt == 0) { - clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_time, NULL); /* wait a short time if no packets */ - } else { - /* local timestamp generation until we get accurate GPS time */ - clock_gettime(CLOCK_REALTIME, &fetch_time); - x = gmtime(&(fetch_time.tv_sec)); - sprintf(fetch_timestamp,"%04i-%02i-%02i %02i:%02i:%02i.%03liZ",(x->tm_year)+1900,(x->tm_mon)+1,x->tm_mday,x->tm_hour,x->tm_min,x->tm_sec,(fetch_time.tv_nsec)/1000000); /* ISO 8601 format */ - } - - /* log packets */ - for (i=0; i < nb_pkt; ++i) { - p = &rxpkt[i]; - - /* writing gateway ID */ - fprintf(log_file, "%016llX,", lgwm); - - /* writing node MAC address */ - fputs("\"\",", log_file); // TODO: need to parse payload - - /* writing UTC timestamp*/ - fprintf(log_file, "\"%s\",", fetch_timestamp); - // TODO: replace with GPS time when available - - /* writing internal clock */ - fprintf(log_file, "%010u,", p->count_us); - - /* writing RX frequency */ - fprintf(log_file, "%10u,", p->freq_hz); - - /* writing RF chain */ - fprintf(log_file, "%u,", p->rf_chain); - - /* writing RX modem/IF chain */ - fprintf(log_file, "%2d,", p->if_chain); - - /* writing status */ - switch(p->status) { - case STAT_CRC_OK: fputs("\"CRC_OK\" ,", log_file); break; - case STAT_CRC_BAD: fputs("\"CRC_BAD\",", log_file); break; - case STAT_NO_CRC: fputs("\"NO_CRC\" ,", log_file); break; - case STAT_UNDEFINED:fputs("\"UNDEF\" ,", log_file); break; - default: fputs("\"ERR\" ,", log_file); - } - - /* writing payload size */ - fprintf(log_file, "%3u,", p->size); - - /* writing modulation */ - switch(p->modulation) { - case MOD_LORA: fputs("\"LORA\",", log_file); break; - case MOD_FSK: fputs("\"FSK\" ,", log_file); break; - default: fputs("\"ERR\" ,", log_file); - } - - /* writing bandwidth */ - switch(p->bandwidth) { - case BW_500KHZ: fputs("500000,", log_file); break; - case BW_250KHZ: fputs("250000,", log_file); break; - case BW_125KHZ: fputs("125000,", log_file); break; - case BW_62K5HZ: fputs("62500 ,", log_file); break; - case BW_31K2HZ: fputs("31200 ,", log_file); break; - case BW_15K6HZ: fputs("15600 ,", log_file); break; - case BW_7K8HZ: fputs("7800 ,", log_file); break; - case BW_UNDEFINED: fputs("0 ,", log_file); break; - default: fputs("-1 ,", log_file); - } - - /* writing datarate */ - if (p->modulation == MOD_LORA) { - switch (p->datarate) { - case DR_LORA_SF7: fputs("\"SF7\" ,", log_file); break; - case DR_LORA_SF8: fputs("\"SF8\" ,", log_file); break; - case DR_LORA_SF9: fputs("\"SF9\" ,", log_file); break; - case DR_LORA_SF10: fputs("\"SF10\" ,", log_file); break; - case DR_LORA_SF11: fputs("\"SF11\" ,", log_file); break; - case DR_LORA_SF12: fputs("\"SF12\" ,", log_file); break; - default: fputs("\"ERR\" ,", log_file); - } - } else if (p->modulation == MOD_FSK) { - fprintf(log_file, "\"%6u\",", p->datarate); - } else { - fputs("\"ERR\" ,", log_file); - } - - /* writing coderate */ - switch (p->coderate) { - case CR_LORA_4_5: fputs("\"4/5\",", log_file); break; - case CR_LORA_4_6: fputs("\"2/3\",", log_file); break; - case CR_LORA_4_7: fputs("\"4/7\",", log_file); break; - case CR_LORA_4_8: fputs("\"1/2\",", log_file); break; - case CR_UNDEFINED: fputs("\"\" ,", log_file); break; - default: fputs("\"ERR\",", log_file); - } - - /* writing packet RSSI */ - fprintf(log_file, "%+.0f,", p->rssi); - - /* writing packet average SNR */ - fprintf(log_file, "%+5.1f,", p->snr); - - /* writing hex-encoded payload (bundled in 32-bit words) */ - fputs("\"", log_file); - for (j = 0; j < p->size; ++j) { - if ((j > 0) && (j%4 == 0)) fputs("-", log_file); - fprintf(log_file, "%02X", p->payload[j]); - } - fputs("\"\n", log_file); - ++pkt_in_log; - } - - /* check time and rotate log file if necessary */ - ++time_check; - if (time_check >= 8) { - time_check = 0; - time(&now_time); - if (difftime(now_time, log_start_time) > log_rotate_interval) { - fclose(log_file); - MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log); - pkt_in_log = 0; - open_log(); - } - } - } - - if (exit_sig == 1) { - /* clean up before leaving */ - i = lgw_stop(); - if (i == LGW_HAL_SUCCESS) { - MSG("INFO: concentrator stopped successfully\n"); - } else { - MSG("WARNING: failed to stop concentrator successfully\n"); - } - fclose(log_file); - MSG("INFO: log file %s closed, %lu packet(s) recorded\n", log_file_name, pkt_in_log); - } - - MSG("INFO: Exiting packet logger program\n"); - return EXIT_SUCCESS; -} - -/* --- EOF ------------------------------------------------------------------ */ diff --git a/loragw_pkt_logger/src/parson.c b/loragw_pkt_logger/src/parson.c deleted file mode 100644 index b273ba1..0000000 --- a/loragw_pkt_logger/src/parson.c +++ /dev/null @@ -1,649 +0,0 @@ -/* - Parson ( http://kgabis.github.com/parson/ ) - Copyright (c) 2012 Krzysztof Gabis - - 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. -*/ - -#include "parson.h" - -#include -#include -#include -#include - -#define ERROR 0 -#define SUCCESS 1 -#define STARTING_CAPACITY 15 -#define ARRAY_MAX_CAPACITY 122880 /* 15*(2^13) */ -#define OBJECT_MAX_CAPACITY 960 /* 15*(2^6) */ -#define MAX_NESTING 19 -#define sizeof_token(a) (sizeof(a) - 1) -#define skip_char(str) ((*str)++) -#define skip_whitespaces(str) while (isspace(**str)) { skip_char(str); } -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -#define parson_malloc(a) malloc(a) -#define parson_free(a) free((void*)a) -#define parson_realloc(a, b) realloc(a, b) - -/* Type definitions */ -typedef union json_value_value { - const char *string; - double number; - JSON_Object *object; - JSON_Array *array; - int boolean; - int null; -} JSON_Value_Value; - -struct json_value_t { - JSON_Value_Type type; - JSON_Value_Value value; -}; - -struct json_object_t { - const char **names; - JSON_Value **values; - size_t count; - size_t capacity; -}; - -struct json_array_t { - JSON_Value **items; - size_t count; - size_t capacity; -}; - -/* Various */ -static int try_realloc(void **ptr, size_t new_size); -static char * parson_strndup(const char *string, size_t n); -static int is_utf(const unsigned char *string); -static int is_decimal(const char *string, size_t length); - -/* JSON Object */ -static JSON_Object * json_object_init(void); -static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value); -static int json_object_resize(JSON_Object *object, size_t capacity); -static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n); -static void json_object_free(JSON_Object *object); - -/* JSON Array */ -static JSON_Array * json_array_init(void); -static int json_array_add(JSON_Array *array, JSON_Value *value); -static int json_array_resize(JSON_Array *array, size_t capacity); -static void json_array_free(JSON_Array *array); - -/* JSON Value */ -static JSON_Value * json_value_init_object(void); -static JSON_Value * json_value_init_array(void); -static JSON_Value * json_value_init_string(const char *string); -static JSON_Value * json_value_init_number(double number); -static JSON_Value * json_value_init_boolean(int boolean); -static JSON_Value * json_value_init_null(void); - -/* Parser */ -static void skip_quotes(const char **string); -static const char * get_processed_string(const char **string); -static JSON_Value * parse_object_value(const char **string, size_t nesting); -static JSON_Value * parse_array_value(const char **string, size_t nesting); -static JSON_Value * parse_string_value(const char **string); -static JSON_Value * parse_boolean_value(const char **string); -static JSON_Value * parse_number_value(const char **string); -static JSON_Value * parse_null_value(const char **string); -static JSON_Value * parse_value(const char **string, size_t nesting); - -/* Various */ -static int try_realloc(void **ptr, size_t new_size) { - void *reallocated_ptr = parson_realloc(*ptr, new_size); - if (!reallocated_ptr) { return ERROR; } - *ptr = reallocated_ptr; - return SUCCESS; -} - -static char * parson_strndup(const char *string, size_t n) { - char *output_string = (char*)parson_malloc(n + 1); - if (!output_string) { return NULL; } - output_string[n] = '\0'; - strncpy(output_string, string, n); - return output_string; -} - -static int is_utf(const unsigned char *s) { - return isxdigit(s[0]) && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]); -} - -static int is_decimal(const char *string, size_t length) { - if (length > 1 && string[0] == '0' && string[1] != '.') { return 0; } - if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { return 0; } - while (length--) { if (strchr("xX", string[length])) { return 0; } } - return 1; -} - -/* JSON Object */ -static JSON_Object * json_object_init(void) { - JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); - if (!new_obj) { return NULL; } - new_obj->names = (const char**)NULL; - new_obj->values = (JSON_Value**)NULL; - new_obj->capacity = 0; - new_obj->count = 0; - return new_obj; -} - -static int json_object_add(JSON_Object *object, const char *name, JSON_Value *value) { - size_t index; - if (object->count >= object->capacity) { - size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY); - if (new_capacity > OBJECT_MAX_CAPACITY) { return ERROR; } - if (json_object_resize(object, new_capacity) == ERROR) { return ERROR; } - } - if (json_object_get_value(object, name) != NULL) { return ERROR; } - index = object->count; - object->names[index] = parson_strndup(name, strlen(name)); - if (!object->names[index]) { return ERROR; } - object->values[index] = value; - object->count++; - return SUCCESS; -} - -static int json_object_resize(JSON_Object *object, size_t capacity) { - if (try_realloc((void**)&object->names, capacity * sizeof(char*)) == ERROR) { return ERROR; } - if (try_realloc((void**)&object->values, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; } - object->capacity = capacity; - return SUCCESS; -} - -static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n) { - size_t i, name_length; - for (i = 0; i < json_object_get_count(object); i++) { - name_length = strlen(object->names[i]); - if (name_length != n) { continue; } - if (strncmp(object->names[i], name, n) == 0) { return object->values[i]; } - } - return NULL; -} - -static void json_object_free(JSON_Object *object) { - while(object->count--) { - parson_free(object->names[object->count]); - json_value_free(object->values[object->count]); - } - parson_free(object->names); - parson_free(object->values); - parson_free(object); -} - -/* JSON Array */ -static JSON_Array * json_array_init(void) { - JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array)); - if (!new_array) { return NULL; } - new_array->items = (JSON_Value**)NULL; - new_array->capacity = 0; - new_array->count = 0; - return new_array; -} - -static int json_array_add(JSON_Array *array, JSON_Value *value) { - if (array->count >= array->capacity) { - size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY); - if (new_capacity > ARRAY_MAX_CAPACITY) { return ERROR; } - if (!json_array_resize(array, new_capacity)) { return ERROR; } - } - array->items[array->count] = value; - array->count++; - return SUCCESS; -} - -static int json_array_resize(JSON_Array *array, size_t capacity) { - if (try_realloc((void**)&array->items, capacity * sizeof(JSON_Value*)) == ERROR) { return ERROR; } - array->capacity = capacity; - return SUCCESS; -} - -static void json_array_free(JSON_Array *array) { - while (array->count--) { json_value_free(array->items[array->count]); } - parson_free(array->items); - parson_free(array); -} - -/* JSON Value */ -static JSON_Value * json_value_init_object(void) { - JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { return NULL; } - new_value->type = JSONObject; - new_value->value.object = json_object_init(); - if (!new_value->value.object) { parson_free(new_value); return NULL; } - return new_value; -} - -static JSON_Value * json_value_init_array(void) { - JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { return NULL; } - new_value->type = JSONArray; - new_value->value.array = json_array_init(); - if (!new_value->value.array) { parson_free(new_value); return NULL; } - return new_value; -} - -static JSON_Value * json_value_init_string(const char *string) { - JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { return NULL; } - new_value->type = JSONString; - new_value->value.string = string; - return new_value; -} - -static JSON_Value * json_value_init_number(double number) { - JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { return NULL; } - new_value->type = JSONNumber; - new_value->value.number = number; - return new_value; -} - -static JSON_Value * json_value_init_boolean(int boolean) { - JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { return NULL; } - new_value->type = JSONBoolean; - new_value->value.boolean = boolean; - return new_value; -} - -static JSON_Value * json_value_init_null(void) { - JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value)); - if (!new_value) { return NULL; } - new_value->type = JSONNull; - return new_value; -} - -/* Parser */ -static void skip_quotes(const char **string) { - skip_char(string); - while (**string != '\"') { - if (**string == '\0') { return; } - if (**string == '\\') { skip_char(string); if (**string == '\0') { return; }} - skip_char(string); - } - skip_char(string); -} - -/* Returns contents of a string inside double quotes and parses escaped - characters inside. - Example: "\u006Corem ipsum" -> lorem ipsum */ -static const char * get_processed_string(const char **string) { - const char *string_start = *string; - char *output, *processed_ptr, *unprocessed_ptr, current_char; - unsigned int utf_val; - skip_quotes(string); - if (**string == '\0') { return NULL; } - output = parson_strndup(string_start + 1, *string - string_start - 2); - if (!output) { return NULL; } - processed_ptr = unprocessed_ptr = output; - while (*unprocessed_ptr) { - current_char = *unprocessed_ptr; - if (current_char == '\\') { - unprocessed_ptr++; - current_char = *unprocessed_ptr; - switch (current_char) { - case '\"': case '\\': case '/': break; - case 'b': current_char = '\b'; break; - case 'f': current_char = '\f'; break; - case 'n': current_char = '\n'; break; - case 'r': current_char = '\r'; break; - case 't': current_char = '\t'; break; - case 'u': - unprocessed_ptr++; - if (!is_utf((const unsigned char*)unprocessed_ptr) || - sscanf(unprocessed_ptr, "%4x", &utf_val) == EOF) { - parson_free(output); return NULL; - } - if (utf_val < 0x80) { - current_char = utf_val; - } else if (utf_val < 0x800) { - *processed_ptr++ = (utf_val >> 6) | 0xC0; - current_char = ((utf_val | 0x80) & 0xBF); - } else { - *processed_ptr++ = (utf_val >> 12) | 0xE0; - *processed_ptr++ = (((utf_val >> 6) | 0x80) & 0xBF); - current_char = ((utf_val | 0x80) & 0xBF); - } - unprocessed_ptr += 3; - break; - default: - parson_free(output); - return NULL; - break; - } - } else if ((unsigned char)current_char < 0x20) { /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */ - parson_free(output); - return NULL; - } - *processed_ptr = current_char; - processed_ptr++; - unprocessed_ptr++; - } - *processed_ptr = '\0'; - if (try_realloc((void**)&output, strlen(output) + 1) == ERROR) { return NULL; } - return output; -} - -static JSON_Value * parse_value(const char **string, size_t nesting) { - if (nesting > MAX_NESTING) { return NULL; } - skip_whitespaces(string); - switch (**string) { - case '{': - return parse_object_value(string, nesting + 1); - case '[': - return parse_array_value(string, nesting + 1); - case '\"': - return parse_string_value(string); - case 'f': case 't': - return parse_boolean_value(string); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return parse_number_value(string); - case 'n': - return parse_null_value(string); - default: - return NULL; - } -} - -static JSON_Value * parse_object_value(const char **string, size_t nesting) { - JSON_Value *output_value = json_value_init_object(), *new_value = NULL; - JSON_Object *output_object = json_value_get_object(output_value); - const char *new_key = NULL; - if (!output_value) { return NULL; } - skip_char(string); - skip_whitespaces(string); - if (**string == '}') { skip_char(string); return output_value; } /* empty object */ - while (**string != '\0') { - new_key = get_processed_string(string); - skip_whitespaces(string); - if (!new_key || **string != ':') { - json_value_free(output_value); - return NULL; - } - skip_char(string); - new_value = parse_value(string, nesting); - if (!new_value) { - parson_free(new_key); - json_value_free(output_value); - return NULL; - } - if(!json_object_add(output_object, new_key, new_value)) { - parson_free(new_key); - parson_free(new_value); - json_value_free(output_value); - return NULL; - } - parson_free(new_key); - skip_whitespaces(string); - if (**string != ',') { break; } - skip_char(string); - skip_whitespaces(string); - } - skip_whitespaces(string); - if (**string != '}' || /* Trim object after parsing is over */ - json_object_resize(output_object, json_object_get_count(output_object)) == ERROR) { - json_value_free(output_value); - return NULL; - } - skip_char(string); - return output_value; -} - -static JSON_Value * parse_array_value(const char **string, size_t nesting) { - JSON_Value *output_value = json_value_init_array(), *new_array_value = NULL; - JSON_Array *output_array = json_value_get_array(output_value); - if (!output_value) { return NULL; } - skip_char(string); - skip_whitespaces(string); - if (**string == ']') { /* empty array */ - skip_char(string); - return output_value; - } - while (**string != '\0') { - new_array_value = parse_value(string, nesting); - if (!new_array_value) { - json_value_free(output_value); - return NULL; - } - if(json_array_add(output_array, new_array_value) == ERROR) { - parson_free(new_array_value); - json_value_free(output_value); - return NULL; - } - skip_whitespaces(string); - if (**string != ',') { break; } - skip_char(string); - skip_whitespaces(string); - } - skip_whitespaces(string); - if (**string != ']' || /* Trim array after parsing is over */ - json_array_resize(output_array, json_array_get_count(output_array)) == ERROR) { - json_value_free(output_value); - return NULL; - } - skip_char(string); - return output_value; -} - -static JSON_Value * parse_string_value(const char **string) { - const char *new_string = get_processed_string(string); - if (!new_string) { return NULL; } - return json_value_init_string(new_string); -} - -static JSON_Value * parse_boolean_value(const char **string) { - size_t true_token_size = sizeof_token("true"); - size_t false_token_size = sizeof_token("false"); - if (strncmp("true", *string, true_token_size) == 0) { - *string += true_token_size; - return json_value_init_boolean(1); - } else if (strncmp("false", *string, false_token_size) == 0) { - *string += false_token_size; - return json_value_init_boolean(0); - } - return NULL; -} - -static JSON_Value * parse_number_value(const char **string) { - char *end; - double number = strtod(*string, &end); - JSON_Value *output_value; - if (is_decimal(*string, end - *string)) { - *string = end; - output_value = json_value_init_number(number); - } else { - output_value = NULL; - } - return output_value; -} - -static JSON_Value * parse_null_value(const char **string) { - size_t token_size = sizeof_token("null"); - if (strncmp("null", *string, token_size) == 0) { - *string += token_size; - return json_value_init_null(); - } - return NULL; -} - -/* Parser API */ -JSON_Value * json_parse_file(const char *filename) { - FILE *fp = fopen(filename, "r"); - size_t file_size; - char *file_contents; - JSON_Value *output_value; - if (!fp) { return NULL; } - fseek(fp, 0L, SEEK_END); - file_size = ftell(fp); - rewind(fp); - file_contents = (char*)parson_malloc(sizeof(char) * (file_size + 1)); - if (!file_contents) { fclose(fp); return NULL; } - if (fread(file_contents, file_size, 1, fp) < 1) { - if (ferror(fp)) { fclose(fp); return NULL; } - } - fclose(fp); - file_contents[file_size] = '\0'; - output_value = json_parse_string(file_contents); - parson_free(file_contents); - return output_value; -} - -JSON_Value * json_parse_string(const char *string) { - if (!string || (*string != '{' && *string != '[')) { return NULL; } - return parse_value((const char**)&string, 0); -} - -/* JSON Object API */ -JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) { - return json_object_nget_value(object, name, strlen(name)); -} - -const char * json_object_get_string(const JSON_Object *object, const char *name) { - return json_value_get_string(json_object_get_value(object, name)); -} - -double json_object_get_number(const JSON_Object *object, const char *name) { - return json_value_get_number(json_object_get_value(object, name)); -} - -JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) { - return json_value_get_object(json_object_get_value(object, name)); -} - -JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) { - return json_value_get_array(json_object_get_value(object, name)); -} - -int json_object_get_boolean(const JSON_Object *object, const char *name) { - return json_value_get_boolean(json_object_get_value(object, name)); -} - -JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) { - const char *dot_position = strchr(name, '.'); - if (!dot_position) { return json_object_get_value(object, name); } - object = json_value_get_object(json_object_nget_value(object, name, dot_position - name)); - return json_object_dotget_value(object, dot_position + 1); -} - -const char * json_object_dotget_string(const JSON_Object *object, const char *name) { - return json_value_get_string(json_object_dotget_value(object, name)); -} - -double json_object_dotget_number(const JSON_Object *object, const char *name) { - return json_value_get_number(json_object_dotget_value(object, name)); -} - -JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) { - return json_value_get_object(json_object_dotget_value(object, name)); -} - -JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) { - return json_value_get_array(json_object_dotget_value(object, name)); -} - -int json_object_dotget_boolean(const JSON_Object *object, const char *name) { - return json_value_get_boolean(json_object_dotget_value(object, name)); -} - -size_t json_object_get_count(const JSON_Object *object) { - return object ? object->count : 0; -} - -const char * json_object_get_name(const JSON_Object *object, size_t index) { - if (index >= json_object_get_count(object)) { return NULL; } - return object->names[index]; -} - -/* JSON Array API */ -JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) { - if (index >= json_array_get_count(array)) { return NULL; } - return array->items[index]; -} - -const char * json_array_get_string(const JSON_Array *array, size_t index) { - return json_value_get_string(json_array_get_value(array, index)); -} - -double json_array_get_number(const JSON_Array *array, size_t index) { - return json_value_get_number(json_array_get_value(array, index)); -} - -JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) { - return json_value_get_object(json_array_get_value(array, index)); -} - -JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) { - return json_value_get_array(json_array_get_value(array, index)); -} - -int json_array_get_boolean(const JSON_Array *array, size_t index) { - return json_value_get_boolean(json_array_get_value(array, index)); -} - -size_t json_array_get_count(const JSON_Array *array) { - return array ? array->count : 0; -} - -/* JSON Value API */ -JSON_Value_Type json_value_get_type(const JSON_Value *value) { - return value ? value->type : JSONError; -} - -JSON_Object * json_value_get_object(const JSON_Value *value) { - return json_value_get_type(value) == JSONObject ? value->value.object : NULL; -} - -JSON_Array * json_value_get_array(const JSON_Value *value) { - return json_value_get_type(value) == JSONArray ? value->value.array : NULL; -} - -const char * json_value_get_string(const JSON_Value *value) { - return json_value_get_type(value) == JSONString ? value->value.string : NULL; -} - -double json_value_get_number(const JSON_Value *value) { - return json_value_get_type(value) == JSONNumber ? value->value.number : 0; -} - -int json_value_get_boolean(const JSON_Value *value) { - return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1; -} - -void json_value_free(JSON_Value *value) { - switch (json_value_get_type(value)) { - case JSONObject: - json_object_free(value->value.object); - break; - case JSONString: - if (value->value.string) { parson_free(value->value.string); } - break; - case JSONArray: - json_array_free(value->value.array); - break; - default: - break; - } - parson_free(value); -} -- cgit v1.2.3