diff --git a/ADIS_ESP32_Eclipse/CMakeLists.txt b/ADIS_ESP32_Eclipse/CMakeLists.txt index 97f9e19..313b93f 100644 --- a/ADIS_ESP32_Eclipse/CMakeLists.txt +++ b/ADIS_ESP32_Eclipse/CMakeLists.txt @@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 3.5) # add my own component direcotry to the list list(APPEND EXTRA_COMPONENT_DIRS "../McuLib") +list(APPEND EXTRA_COMPONENT_DIRS "lib") include($ENV{IDF_PATH}/tools/cmake/project.cmake) # additional include for IncludeMcuLibConfig diff --git a/ADIS_ESP32_Eclipse/lib/CMakeLists.txt b/ADIS_ESP32_Eclipse/lib/CMakeLists.txt new file mode 100644 index 0000000..90cf443 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register( + SRCS + "microjson-1.6/mjson.c" + + INCLUDE_DIRS + "./microjson-1.6" +) \ No newline at end of file diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/COPYING b/ADIS_ESP32_Eclipse/lib/microjson-1.6/COPYING new file mode 100644 index 0000000..0498485 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/COPYING @@ -0,0 +1,27 @@ + BSD LICENSE + +Copyright (c) 2015, Eric S. Raymond +All rights reserved. + +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. + +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. diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/Makefile b/ADIS_ESP32_Eclipse/lib/microjson-1.6/Makefile new file mode 100644 index 0000000..cd3d71d --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/Makefile @@ -0,0 +1,66 @@ +# Makefile for the microjson project + +# The version for release is derived from the mostt recent stanza in the news file. +VERSION=$(shell sed -n +#include +#include + +#include "mjson.h" + +static bool flag1, flag2; +static int count; + +static const struct json_attr_t json_attrs[] = { + {"count", t_integer, .addr.integer = &count}, + {"flag1", t_boolean, .addr.boolean = &flag1,}, + {"flag2", t_boolean, .addr.boolean = &flag2,}, + {NULL}, +}; + +int main(int argc, char *argv[]) +{ + int status = 0; + + status = json_read_object(argv[1], json_attrs, NULL); + printf("status = %d, count = %d, flag1 = %d, flag2 = %d\n", + status, count, flag1, flag2); + if (status != 0) + puts(json_error_string(status)); +} + +/* end */ diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/example2.c b/ADIS_ESP32_Eclipse/lib/microjson-1.6/example2.c new file mode 100644 index 0000000..66ef2f6 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/example2.c @@ -0,0 +1,50 @@ +/* example2.c - second example in the programming guide + * + * This file is Copyright (c) 2014 by Eric S. Raymond + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include + +#include "mjson.h" + +#define MAXCHANNELS 72 + +static bool usedflags[MAXCHANNELS]; +static int PRN[MAXCHANNELS]; +static int elevation[MAXCHANNELS]; +static int azimuth[MAXCHANNELS]; +static int visible; + +const struct json_attr_t sat_attrs[] = { + {"PRN", t_integer, .addr.integer = PRN}, + {"el", t_integer, .addr.integer = elevation}, + {"az", t_integer, .addr.integer = azimuth}, + {"used", t_boolean, .addr.boolean = usedflags}, + {NULL}, +}; + +const struct json_attr_t json_attrs_sky[] = { + {"class", t_check, .dflt.check = "SKY"}, + {"satellites", t_array, .addr.array.element_type = t_object, + .addr.array.arr.objects.subtype=sat_attrs, + .addr.array.maxlen = MAXCHANNELS, + .addr.array.count = &visible}, + {NULL}, + }; + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_read_object(argv[1], json_attrs_sky, NULL); + printf("%d satellites:\n", visible); + for (i = 0; i < visible; i++) + printf("PRN = %d, elevation = %d, azimuth = %d\n", + PRN[i], elevation[i], azimuth[i]); + + if (status != 0) + puts(json_error_string(status)); +} + +/* end */ diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/example3.c b/ADIS_ESP32_Eclipse/lib/microjson-1.6/example3.c new file mode 100644 index 0000000..23adcc2 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/example3.c @@ -0,0 +1,67 @@ +/* example2.c - first example in the programming guide + * + * This file is Copyright (c) 2014 by Eric S. Raymond + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include +#include +#include + +#include "mjson.h" + +#define MAXUSERDEVS 4 + +struct devconfig_t { + char path[PATH_MAX]; + double activated; +}; + +struct devlist_t { + int ndevices; + struct devconfig_t list[MAXUSERDEVS]; +}; + +static struct devlist_t devicelist; + +static int json_devicelist_read(const char *buf) +{ + const struct json_attr_t json_attrs_subdevice[] = { + {"path", t_string, STRUCTOBJECT(struct devconfig_t, path), + .len = sizeof(devicelist.list[0].path)}, + {"activated", t_real, STRUCTOBJECT(struct devconfig_t, activated)}, + {NULL}, + }; + const struct json_attr_t json_attrs_devices[] = { + {"class", t_check,.dflt.check = "DEVICES"}, + {"devices", t_array, STRUCTARRAY(devicelist.list, + json_attrs_subdevice, + &devicelist.ndevices)}, + {NULL}, + }; + int status; + + memset(&devicelist, '\0', sizeof(devicelist)); + status = json_read_object(buf, json_attrs_devices, NULL); + if (status != 0) { + return status; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_devicelist_read(argv[1]); + printf("%d devices:\n", devicelist.ndevices); + for (i = 0; i < devicelist.ndevices; i++) + printf("%s @ %f\n", + devicelist.list[i].path, devicelist.list[i].activated); + + if (status != 0) + puts(json_error_string(status)); +} + +/* end */ diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/example4.c b/ADIS_ESP32_Eclipse/lib/microjson-1.6/example4.c new file mode 100644 index 0000000..fea28a9 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/example4.c @@ -0,0 +1,43 @@ +/* example4.c - fourth example in the programming guide + * + * This file is Copyright (c) 2020 by Eric S. Raymond + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include + +#include "mjson.h" + +#define ARR1_LENGTH 8 + +static bool flag1; +static int arr1[ARR1_LENGTH]; +static int arr1_count; + +const struct json_attr_t json_attrs_example4[] = { + {"flag1", t_boolean, .addr.boolean = &flag1}, + {"arr1", t_array, .addr.array.element_type = t_integer, + .addr.array.arr.integers = arr1, + .addr.array.maxlen = ARR1_LENGTH, + .addr.array.count = &arr1_count}, + {NULL}, +}; + +int main(int argc, char *argv[]) +{ + const char* end = (const char*) argv[1] + strlen((const char*) argv[1]); + const char* cur = (const char*) argv[1]; + + while (cur < end) { + int status = json_read_object(cur, json_attrs_example4, &cur); + printf("status: %d, flag1: %d\n", status, flag1); + for (int i = 0; i < arr1_count; i++) + printf("arr1 = %d\n", arr1[i]); + if (status != 0) + puts(json_error_string(status)); + arr1_count = 0; + } +} + +/* end */ diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/microjson.adoc b/ADIS_ESP32_Eclipse/lib/microjson-1.6/microjson.adoc new file mode 100644 index 0000000..9a6ffd2 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/microjson.adoc @@ -0,0 +1,497 @@ += Building static JSON parsers with microjson = +Eric S. Raymond + +== Overview == + +microjson is a tiny parser for the largest subset of JSON (JavaScript Object +Notation) that can be unpacked to C static storage. It uses entirely +fixed-extent memory, no malloc(). It is thus very suitable for use in +memory-constrained environments such as embedded systems; also for +long-running service daemons that must provably not leak memory. + +microjson is extremely well-tested code. This is essentially the same +parser used in GPSD and its client libraries, which have hundreds of +millions of deployments underneath Google Maps in Android phones. + +microjson parses JSON from string input and unpacks the content +directly into static storage declared by the calling program. +You give it a set of template structures describing the expected shape +of the incoming JSON, and it will error out if that shape is not +matched. When the parse succeeds, attribute values will be extracted +into static locations specified in the template structures. + +== How To Use This Document == + +This is a fast tutorial for working programmers. It teaches by +examples; if you read the code carefully it will tell you +more than the accompanying text. Just read it in sequence, trying not +to skip anything. + +All the examples are shipped in the microjson distribution. Most are +not synthetic toys, but stripped-down versions of working code from +GPSD. Copy them freely. You may also want to look at the source for +test_microjson.c, the regression test; it exercises most cases. + +== An Example == + +Here is nearly the simplest possible example: + +.Example 1 +--------------------------------------------------------------------- +/* + * Parse JSON shaped like '{"flag1":true,"flag2":false,"count":42}' + */ + +#include +#include +#include + +#include "mjson.h" + +static bool flag1, flag2; +static int count; + +static const struct json_attr_t json_attrs[] = { + {"count", t_integer, .addr.integer = &count}, + {"flag1", t_boolean, .addr.boolean = &flag1,}, + {"flag2", t_boolean, .addr.boolean = &flag2,}, + {NULL}, +}; + +int main(int argc, char *argv[]) +{ + int status = 0; + + status = json_read_object(argv[1], json_attrs, NULL); + printf("status = %d, count = %d, flag1 = %d, flag2 = %d\n", + status, count, flag1, flag2); + if (status != 0) + puts(json_error_string(status)); +} +--------------------------------------------------------------------- + +And here are some invocations: + +--------------------------------------------------------------------- +$ example1 '{"flag1":true,"flag2":false,"count":42}' +status = 0, count = 42, flag1 = 1, flag2 = 0 + +$ example1 '{"flag1":true,"flag2":false,"count":23}' +status = 0, count = 23, flag1 = 1, flag2 = 0 + +$ example1 '{"whozis":true,"flag2":false,"count":23}' +status = 3, count = 0, flag1 = 0, flag2 = 0 +unknown attribute name + +$ example1 '{"flag1":true,"flag2":false,"count":23,"whozis":"whatsis"}' +status = 3, count = 23, flag1 = 1, flag2 = 0 +unknown attribute name + +--------------------------------------------------------------------- + +The +json_read_object()+ call unpacks the values in the argument JSON +object into three static variables. In many uses the target locations +would instead be storage in some static structure instance. + +In this example, the +json_attrs+ structure array associates each +possible member name with a type and a target address. The function ++json_read_object()+ treats this array of constants as parsing +instructions. + +When an unexpected attribute name is encountered, the parser normally +terminates, returning an error status (but it is possible to mage the +parser ignore unknown attributes instead). Attributes and values +parsed before a terminating error modify their target storage. + +The parser recognizes a wider range of types than this, and the +template structures can specify defaults when an expected JSON +attribute is omitted. Most of the rest is details. + +== Theory of Operation == + +The parser is a simple state machine that walks the input looking +for syntactically well-formed attribute-value pairs. Each time it +finds one, it looks up the name in the template structure array +driving the parse. The type tells it how to interpret the +value; the target address tells it where to put the value. + +Syntax errors, or any unknown attribute name, terminate the parse. +That is unless the wildcard ignore option is used. + +One consequence to be aware of is that if an input JSON object +contains multiple attribute-value pairs with the same attribute, +the associated storage will be modified each time and only +the last setting will be effective. + +=== Simple Value Types === + +The type field of a +json_attr_t+ structure can have the following +'simple' alternatives, each corresponding to an atomic JSON value: + ++t_check+: Value of this attribute must match a specified string, +or the parse will fail with a distinguishable error. + ++t_integer+: Parse a single signed integer literal, copy the value +to a C +int+ location. Uses +strtol()+. + ++t_uinteger+: Parse a single signed integer literal, copy the value +to a C +unsigned int+ location. Uses +strtoul()+. + ++t_real+: Parse a single signed float literal, copy the value +to a C +double+ location. Uses +strtod()+. + ++t_boolean+: Accept one of the JSON literals +true+ or +false+, +copy the value to a C +bool+ location. + ++t_string+: Accept a JSON string literal, copy the contents to a +C char buffer. + ++t_character+: Accept a single-character JSON string literal, copy +that character to a C +char+ location. + ++t_time+" Accept a string that is an RFC3339 timestamp (full ISO-8601 +date/time in Zulu time with optional fractional decimal seconds). +Store as a double value, seconds since Unix epoch. Accepted only +if the code was built with -DTIME_ENABLE; introduces a dependency +on the glibc function timegm(). + +Associated with each simple value type's storage (in the +addr+ +union) is a correspondingly-named field in the +dflt+ union). +This is a default value which is copied to the target storage +when the JSON object does not contain the corresponding attribute. +You can turn off this defaulting behavior by setting the +nodefault+ +member to +true+. + +=== Enumerated-value types === + +The parser includes support for string attributes with controlled +vocabularies. + +A +json_attr_t+ instance with a +t_integer+ or +t_uinteger+ type field +can point at a map (an array of +json_enum_t+ structures) that lists +names and pairs of integral values. If this is done, the parser +expects the values of the JSON attribute to be strings but internally +maps them to corresponding integer values before setting the target +storage. An un-enumerated string value causes the parse to error out. + +(Case 8 in the unit test source code illustrates how to use this feature.) + +=== Compound Value Types === + +The following cases do not parse JSON value atoms: + +==== Skip fields ==== + +t_ignore: Value of this attribute is ignored. Significant because +unexpected attribute names cause the parse to terminate with error. +An empty attribute name may be used to wildcard ignore all unknown +fields. This should rarely be used and always as penultimate to the +terminating NULL. + +==== Sub-objects ==== + +t_object: It is possible to parse JSON objects within JSON objects. +See case 14 in the unit test for an example. + +==== Parallel arrays === + +t_array: Value of this attribute is expected to be a homogenous array. +Another field of the structure specifies the array's element type, +which can be any simple type or t_object (meaning a JSON subobject). + +If the array has simple elements, three additional things must be +specified: the base address of the array's storage, the maximum number +of elements it can have, and an integer address where the parser will +place a count of elements filled in. + +Simple array values always default to zero for numeric types, +false+ +for booleans, and NULL for strings. + +The array element type may be +t_object+, as in the +satellites+ field +in this example: + +.Example 2 +------------------------------------------------------------------------ +#include +#include + +#include "mjson.h" + +#define MAXCHANNELS 72 + +static bool usedflags[MAXCHANNELS]; +static int PRN[MAXCHANNELS]; +static int elevation[MAXCHANNELS]; +static int azimuth[MAXCHANNELS]; +static int visible; + +const struct json_attr_t sat_attrs[] = { + {"PRN", t_integer, .addr.integer = PRN}, + {"el", t_integer, .addr.integer = elevation}, + {"az", t_integer, .addr.integer = azimuth}, + {"used", t_boolean, .addr.boolean = usedflags}, + {NULL}, +}; + +const struct json_attr_t json_attrs_sky[] = { + {"class", t_check, .dflt.check = "SKY"}, + {"satellites", t_array, .addr.array.element_type = t_object, + .addr.array.arr.objects.subtype=sat_attrs, + .addr.array.maxlen = MAXCHANNELS, + .addr.array.count = &visible}, + {NULL}, + }; + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_read_object(argv[1], json_attrs_sky, NULL); + printf("%d satellites:\n", visible); + for (i = 0; i < visible; i++) + printf("PRN = %d, elevation = %d, azimuth = %d\n", + PRN[i], elevation[i], azimuth[i]); + + if (status != 0) + puts(json_error_string(status)); +} +------------------------------------------------------------------------ + +Here's an example invocation (string literal folded for readability): + +-------------------------------------------------------- +$ example2 '{"class":"SKY","satellites": + [{"PRN":10,"el":45,"az":196,"used":true}, + {"PRN":29,"el":67,"az":310,"used":true}]}' +2 satellites: +PRN = 10, elevation = 45, azimuth = 196 +PRN = 29, elevation = 67, azimuth = 310 +-------------------------------------------------------- + +In this case, the parser needs to be told where to find a template +array describing how to parse the element objects. The target addresses +in this structure will point to the base addressees of parallel arrays. +The arrays are filled in until the parser runs out of conforming JSON +sub-objects to parse or would exceed the +maxlen+ count of elements. + +More formally: parallel object arrays take one base address per object +subfield, and are mapped into parallel C arrays (one per subfield). +Strings are not supported in this kind of array, as they don't have a +"natural" fixed size to use as an offset multiplier. + +The default of array elements is always zero (false for booleans, NULL +for strings). + +==== Structure arrays ==== + +There's a different way to parse arrays that can unpack an +array of JSON objects directly into an array of C structs. + +.Example 3: +-------------------------------------------------------- +#include +#include +#include +#include +#include +#include +#include + + +#include "mjson.h" + +#define MAXUSERDEVS 4 + +struct devconfig_t { + char path[PATH_MAX]; + double activated; +}; + +struct devlist_t { + int ndevices; + struct devconfig_t list[MAXUSERDEVS]; +}; + +static struct devlist_t devicelist; + +static int json_devicelist_read(const char *buf) +{ + const struct json_attr_t json_attrs_subdevice[] = { + {"path", t_string, STRUCTOBJECT(struct devconfig_t, path), + .len = sizeof(devicelist.list[0].path)}, + {"activated", t_real, STRUCTOBJECT(struct devconfig_t, activated)}, + {NULL}, + }; + const struct json_attr_t json_attrs_devices[] = { + {"class", t_check,.dflt.check = "DEVICES"}, + {"devices", t_array, STRUCTARRAY(devicelist.list, + json_attrs_subdevice, + &devicelist.ndevices)}, + {NULL}, + }; + int status; + + memset(&devicelist, '\0', sizeof(devicelist)); + status = json_read_object(buf, json_attrs_devices, NULL); + if (status != 0) { + return status; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + status = json_devicelist_read(argv[1]); + printf("%d devices:\n", devicelist.ndevices); + for (i = 0; i < devicelist.ndevices; i++) + printf("%s @ %f\n", + devicelist.list[i].path, devicelist.list[i].activated); + + if (status != 0) + puts(json_error_string(status)); +} +-------------------------------------------------------- + +Here is an example: + +-------------------------------------------------------- +$ example3 '{"devices":[{"path":"/dev/ttyUSB0", + "activated":1411468340}]}' +1 devices: +/dev/ttyUSB0 @ 1411468340.000000 +-------------------------------------------------------- + +In this case, the STRUCTARRAY and STRUCTOBJECT macros are clues to +what is going on. STRUCTOBJECT is a thin wrapper around offsetof(); +STRUCTARRAY sets up the parser to walk through the array of +structures, filling each element as it goes. + +More formally: structobject arrays are a way to parse a list of +objects to a set of modifications to a corresponding array of C +structs. The trick is that the array object initialization has to +specify both the C struct array's base address and the stride length +(the size of the C struct). If you initialize the offset fields with +the correct offsetof calls, everything will work. Strings are +supported but all string storage has to be inline in the struct. + +== Parsing Concatenated Objects == + +The +end+ param of +json_read_object()+ can be re-used as the +cp+ param +to the same. As a result, a simple loop can be used to parse streamed or +concatenated root level JSON objects. + +.Example 4 +-------------------------------------------------------- +#include +#include +#include + +#include "mjson.h" + +#define ARR1_LENGTH 8 + +static bool flag1; +static int arr1[ARR1_LENGTH]; +static int arr1_count; + +const struct json_attr_t json_attrs_example4[] = { + {"flag1", t_boolean, .addr.boolean = &flag1}, + {"arr1", t_array, .addr.array.element_type = t_integer, + .addr.array.arr.integers = arr1, + .addr.array.maxlen = ARR1_LENGTH, + .addr.array.count = &arr1_count}, + {NULL}, +}; + +int main(int argc, char *argv[]) +{ + int i, status = 0; + + const char* end = (const char*) argv[1] + strlen((const char*) argv[1]); + const char* cur = (const char*) argv[1]; + + while (cur < end) { + status = json_read_object(cur, json_attrs_example4, &cur); + printf("status: %d, flag1: %d\n", status, flag1); + for (i = 0; i < arr1_count; i++) + printf("arr1 = %d\n", arr1[i]); + if (status != 0) + puts(json_error_string(status)); + arr1_count = 0; + } +} +-------------------------------------------------------- + +Here is an example: + +-------------------------------------------------------- +$ ./example4 '{"flag1":true} {"flag1":0,"arr1":[10,20]} + {"flag1":1} {"flag1":7, "arr1":[30,40,50]' +status: 0, flag1: 1 +status: 0, flag1: 0 +arr1 = 10 +arr1 = 20 +status: 0, flag1: 1 +status: 0, flag1: 1 +arr1 = 30 +arr1 = 40 +arr1 = 50 +-------------------------------------------------------- + +(Test case 18 also illustrates how to use this feature.) + +== Some Grubby Details == + +You have to specify the shape of the JSON you expect to parse in advance. + +The "shape" of a JSON object is the type signature of its +attributes (and attribute values, and so on recursively down through +all nestings of objects and arrays). This parser is indifferent to +the order of attributes at any level, but you have to tell it in +advance what the type of each attribute value will be and where the +parsed value will be stored. The template structures may supply +default values to be used when an expected attribute is omitted. + +The preceding paragraph told one fib. A single attribute may actually +have a span of multiple specifications with different syntactically +distinguishable types (e.g. string vs. real vs. integer vs. boolean, +but not signed integer vs. unsigned integer). The parser will match +the right spec against the actual data. (There's an instance +of this in Example 3.) + +The dialect this parses has some limitations. First, it cannot +recognize the JSON "null" value. Second, all elements of an array must +be of the same type. Third, t_character may not be an array element +(this restriction could be lifted, and might be in a future release). +Third, both attribute names and string values have hard limits; these +can be tweaked by modifying the header file. + +There are separate entry points for beginning a parse of either a JSON +object or a JSON array. + +JSON "float" quantities are actually stored as doubles. Note that +float parsing uses +atof(3)+ and is thus locale-sensitive - this +affects whether period or comma is used as a decimal point. If in any +doubt, set the C numeric locale explicitly to match your data source. + +You should not assume that the numeric values of error codes are +stable. Use the JSON_ERR_* names, not the numbers. + +We have been informed that it is possible to core-dump this code by +passing NULL or bogus pointers to json_read_object(), so don't do +that. There's no sanity check against bad arguments in order to +keep the library small and light. + +== Advanced Usage == + +This code is designed to be stripped down still further; do not be +afraid to copy mjson.c and drop out the parts you don't need (but +please leave in my name somewhere as original author). + +It is a good idea, when possible, to generate your parse-template +structures programmatically from a higher-level description of the +JSON. GPSD uses this technique extensively. + +// end diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.adoc b/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.adoc new file mode 100644 index 0000000..33d5192 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.adoc @@ -0,0 +1,75 @@ += mjson(3) = +:doctype: manpage + +== NAME == +mjson - fast parse of JSON to fixed-extent C structures + +== FUNCTIONS == + +---------------------------------------------------- +#include "mjson.h" + +int json_read_object(const char *, const struct json_attr_t *, const char **); + +int json_read_array(const char *, const struct json_array_t *, const char **); + +const char *json_error_string(int); + +void json_enable_debug(int, FILE *); +---------------------------------------------------- + +== DESCRIPTION == ++json_read_object()+ attempts to parse a whole JSON object from the +buffer pointed at by the first argument. The second argument points +at an array of template structures describing the expected shape of +the JSON object (that is, the set of attribute names and their +expected value types. order-independent) and specifying for each +attribute a C address where the parsed value should ve deivered. +The third argument, if non-null, is where a copy of a pointer +to just past the parsed object is placed. + ++json_read_object()+ attempts to parse a whole JSON object from the +buffer pointed at by the first argument. The second argument points +at a template structure describing the array's element type and where +successive elements are to be placed. The third argument, if non-null, +is where a copy of a pointer to just past the parsed object is placed. + +Objects may contain objects or arrays as attribute values, and an +array may be composed of JSON objects. These functions mutually +recurse as required. (Arrays within arrays are currently not +supported; this may change in a future release.) + ++void json_enable_debug(int, FILE *)+ enables the generation of trace +messages to the indicated file pointer while parsing. + +For details on how to build template structures, consult the document +_Building Static JSON Parsers With Microjson_ shipped with the +distribution. + +== RETURN VALUE == +The two main functions return 0 for success, a nonzero error code for +failure. The function +json_error_string()+ maps error codes to +explanatory messages. + +When an error is returned and the end pointer (third) argument is +non-null, it is filled with the value of the buffer pointer at the +time the error was thrown. + +== BUGS == +A backslash escape of "\0000" in a string value will drop a NUL in the +string that is likely to confuse C code looking at the storage. In +general, the high byte of a \uxxxxx escape will be ignored and the +ASCII high-half byte named by the last two hex digits will be +inserted in the string. + +There is no way to make a parser accept the typeless special JSON +value "null", because there is nothing in C's type ontology to map it +to. + +JSON arrays are restricted to having a single homogenous element type. +However, the type may itself be a JSON object. + +== REPORTING BUGS == +Report bugs to Eric S. Raymond . The project page is +at http://catb.org/~esr/microjson + diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.c b/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.c new file mode 100644 index 0000000..1255cea --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.c @@ -0,0 +1,879 @@ +/**************************************************************************** + +NAME + mjson.c - parse JSON into fixed-extent data structures + +DESCRIPTION + This module parses a large subset of JSON (JavaScript Object +Notation). Unlike more general JSON parsers, it doesn't use malloc(3) +and doesn't support polymorphism; you need to give it a set of +template structures describing the expected shape of the incoming +JSON, and it will error out if that shape is not matched. When the +parse succeeds, attribute values will be extracted into static +locations specified in the template structures. + + The "shape" of a JSON object in the type signature of its +attributes (and attribute values, and so on recursively down through +all nestings of objects and arrays). This parser is indifferent to +the order of attributes at any level, but you have to tell it in +advance what the type of each attribute value will be and where the +parsed value will be stored. The template structures may supply +default values to be used when an expected attribute is omitted. + + The preceding paragraph told one fib. A single attribute may +actually have a span of multiple specifications with different +syntactically distinguishable types (e.g. string vs. real vs. integer +vs. boolean, but not signed integer vs. unsigned integer). The parser +will match the right spec against the actual data. + + The dialect this parses has some limitations. First, it cannot +recognize the JSON "null" value. Second, all elements of an array must +be of the same type. Third, characters may not be array elements (this +restriction could be lifted) + + There are separate entry points for beginning a parse of either +JSON object or a JSON array. JSON "float" quantities are actually +stored as doubles. + + This parser processes object arrays in one of two different ways, +defending on whether the array subtype is declared as object or +structobject. + + Object arrays take one base address per object subfield, and are +mapped into parallel C arrays (one per subfield). Strings are not +supported in this kind of array, as they don't have a "natural" size +to use as an offset multiplier. + + Structobjects arrays are a way to parse a list of objects to a set +of modifications to a corresponding array of C structs. The trick is +that the array object initialization has to specify both the C struct +array's base address and the stride length (the size of the C struct). +If you initialize the offset fields with the correct offsetof calls, +everything will work. Strings are supported but all string storage +has to be inline in the struct. + +PERMISSIONS + This file is Copyright (c) 2014 by Eric S. Raymond + SPDX-License-Identifier: BSD-2-Clause + +***************************************************************************/ +/* The strptime prototype is not provided unless explicitly requested. + * We also need to set the value high enough to signal inclusion of + * newer features (like clock_gettime). See the POSIX spec for more info: + * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_02_01_02 */ +#define _XOPEN_SOURCE 600 + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for HUGE_VAL */ + +#include "mjson.h" + +#define str_starts_with(s, p) (strncmp(s, p, strlen(p)) == 0) + +#ifdef DEBUG_ENABLE +static int debuglevel = 0; +static FILE *debugfp; + +void json_enable_debug(int level, FILE * fp) +/* control the level and destination of debug trace messages */ +{ + debuglevel = level; + debugfp = fp; +} + +static void json_trace(int errlevel, const char *fmt, ...) +/* assemble command in printf(3) style */ +{ + if (errlevel <= debuglevel) { + char buf[BUFSIZ]; + va_list ap; + + (void)strncpy(buf, "json: ", BUFSIZ-1); + buf[BUFSIZ-1] = '\0'; + va_start(ap, fmt); + (void)vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, + ap); + va_end(ap); + + (void)fputs(buf, debugfp); + } +} + +# define json_debug_trace(args) (void) json_trace args +#else +# define json_debug_trace(args) do { } while (0) +#endif /* DEBUG_ENABLE */ + +static char *json_target_address(const struct json_attr_t *cursor, + const struct json_array_t + *parent, int offset) +{ + char *targetaddr = NULL; + if (parent == NULL || parent->element_type != t_structobject) { + /* ordinary case - use the address in the cursor structure */ + switch (cursor->type) { + case t_ignore: + targetaddr = NULL; + break; + case t_integer: + targetaddr = (char *)&cursor->addr.integer[offset]; + break; + case t_uinteger: + targetaddr = (char *)&cursor->addr.uinteger[offset]; + break; + case t_short: + targetaddr = (char *)&cursor->addr.shortint[offset]; + break; + case t_ushort: + targetaddr = (char *)&cursor->addr.ushortint[offset]; + break; +#ifdef TIME_ENABLE + case t_time: +#endif /* TIME_ENABLE */ + case t_real: + targetaddr = (char *)&cursor->addr.real[offset]; + break; + case t_string: + targetaddr = cursor->addr.string; + break; + case t_boolean: + targetaddr = (char *)&cursor->addr.boolean[offset]; + break; + case t_character: + targetaddr = (char *)&cursor->addr.character[offset]; + break; + default: + targetaddr = NULL; + break; + } + } else + /* tricky case - hacking a member in an array of structures */ + targetaddr = + parent->arr.objects.base + (offset * parent->arr.objects.stride) + + cursor->addr.offset; + json_debug_trace((1, "Target address for %s (offset %d) is %p\n", + cursor->attribute, offset, targetaddr)); + return targetaddr; +} + +#ifdef TIME_ENABLE +static double iso8601_to_unix(char *isotime) +/* ISO8601 UTC to Unix UTC */ +{ + double usec; + struct tm tm; + + char *dp = strptime(isotime, "%Y-%m-%dT%H:%M:%S", &tm); + if (dp == NULL) + return (double)HUGE_VAL; + if (*dp == '.') + usec = strtod(dp, NULL); + else + usec = 0; + return (double)timegm(&tm) + usec; +} +#endif /* TIME_ENABLE */ + +static int json_internal_read_object(const char *cp, + const struct json_attr_t *attrs, + const struct json_array_t *parent, + int offset, + const char **end) +{ + enum + { init, await_attr, in_attr, await_value, in_val_string, + in_escape, in_val_token, post_val, post_element + } state = 0; +#ifdef DEBUG_ENABLE + char *statenames[] = { + "init", "await_attr", "in_attr", "await_value", "in_val_string", + "in_escape", "in_val_token", "post_val", "post_element", + }; +#endif /* DEBUG_ENABLE */ + char attrbuf[JSON_ATTR_MAX + 1], *pattr = NULL; + char valbuf[JSON_VAL_MAX + 1], *pval = NULL; + bool value_quoted = false; + char uescape[5]; /* enough space for 4 hex digits and a NUL */ + const struct json_attr_t *cursor; + int substatus, n, maxlen = 0; + unsigned int u; + const struct json_enum_t *mp; + char *lptr; + + if (end != NULL) + *end = NULL; /* give it a well-defined value on parse failure */ + + /* stuff fields with defaults in case they're omitted in the JSON input */ + for (cursor = attrs; cursor->attribute != NULL; cursor++) + if (!cursor->nodefault) { + lptr = json_target_address(cursor, parent, offset); + if (lptr != NULL) + switch (cursor->type) { + case t_integer: + memcpy(lptr, &cursor->dflt.integer, sizeof(int)); + break; + case t_uinteger: + memcpy(lptr, &cursor->dflt.uinteger, sizeof(unsigned int)); + break; + case t_short: + memcpy(lptr, &cursor->dflt.shortint, sizeof(short)); + break; + case t_ushort: + memcpy(lptr, &cursor->dflt.ushortint, + sizeof(unsigned short)); + break; +#ifdef TIME_ENABLE + case t_time: +#endif /* TIME_ENABLE */ + case t_real: + memcpy(lptr, &cursor->dflt.real, sizeof(double)); + break; + case t_string: + if (parent != NULL + && parent->element_type != t_structobject + && offset > 0) + return JSON_ERR_NOPARSTR; + lptr[0] = '\0'; + break; + case t_boolean: + memcpy(lptr, &cursor->dflt.boolean, sizeof(bool)); + break; + case t_character: + lptr[0] = cursor->dflt.character; + break; + case t_object: /* silences a compiler warning */ + case t_structobject: + case t_array: + case t_check: + case t_ignore: + break; + } + } + + json_debug_trace((1, "JSON parse of '%s' begins.\n", cp)); + + /* parse input JSON */ + for (; *cp != '\0'; cp++) { + json_debug_trace((2, "State %-14s, looking at '%c' (%p)\n", + statenames[state], *cp, cp)); + switch (state) { + case init: + if (isspace((unsigned char) *cp)) + continue; + else if (*cp == '{') + state = await_attr; + else { + json_debug_trace((1, + "Non-WS when expecting object start.\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_OBSTART; + } + break; + case await_attr: + if (isspace((unsigned char) *cp)) + continue; + else if (*cp == '"') { + state = in_attr; + pattr = attrbuf; + if (end != NULL) + *end = cp; + } else if (*cp == '}') + break; + else { + json_debug_trace((1, "Non-WS when expecting attribute.\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_ATTRSTART; + } + break; + case in_attr: + if (pattr == NULL) + /* don't update end here, leave at attribute start */ + return JSON_ERR_NULLPTR; + if (*cp == '"') { + *pattr++ = '\0'; + json_debug_trace((1, "Collected attribute name %s\n", + attrbuf)); + for (cursor = attrs; cursor->attribute != NULL; cursor++) { + json_debug_trace((2, "Checking against %s\n", + cursor->attribute)); + if (strcmp(cursor->attribute, attrbuf) == 0) + break; + if (strcmp(cursor->attribute, "") == 0 && + cursor->type == t_ignore) { + break; + } + } + if (cursor->attribute == NULL) { + json_debug_trace((1, + "Unknown attribute name '%s'" + " (attributes begin with '%s').\n", + attrbuf, attrs->attribute)); + /* don't update end here, leave at attribute start */ + return JSON_ERR_BADATTR; + } + state = await_value; + if (cursor->type == t_string) + maxlen = (int)cursor->len - 1; + else if (cursor->type == t_check) + maxlen = (int)strlen(cursor->dflt.check); +#ifdef TIME_ENABLE + else if (cursor->type == t_time) + maxlen = JSON_VAL_MAX; +#endif /* TIME_ENABLE */ + else if (cursor->type == t_ignore) + maxlen = JSON_VAL_MAX; + else if (cursor->map != NULL) + maxlen = (int)sizeof(valbuf) - 1; + pval = valbuf; + } else if (pattr >= attrbuf + JSON_ATTR_MAX - 1) { + json_debug_trace((1, "Attribute name too long.\n")); + /* don't update end here, leave at attribute start */ + return JSON_ERR_ATTRLEN; + } else + *pattr++ = *cp; + break; + case await_value: + if (isspace((unsigned char) *cp) || *cp == ':') + continue; + else if (*cp == '[') { + if (cursor->type != t_array) { + json_debug_trace((1, + "Saw [ when not expecting array.\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_NOARRAY; + } + substatus = json_read_array(cp, &cursor->addr.array, &cp); + if (substatus != 0) + return substatus; + state = post_element; + } else if (cursor->type == t_array) { + json_debug_trace((1, + "Array element was specified, but no [.\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_NOBRAK; + } else if (*cp == '{') { + if (cursor->type != t_object) { + json_debug_trace((1, + "Saw { when not expecting object.\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_NOARRAY; + } + substatus = json_read_object(cp, cursor->addr.attrs, &cp); + if (substatus != 0) + return substatus; + --cp; // last } will be re-consumed by cp++ at end of loop + state = post_element; + } else if (cursor->type == t_object) { + json_debug_trace((1, + "Object element was specified, but no {.\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_NOCURLY; + } else if (*cp == '"') { + value_quoted = true; + state = in_val_string; + pval = valbuf; + } else { + value_quoted = false; + state = in_val_token; + pval = valbuf; + *pval++ = *cp; + } + break; + case in_val_string: + if (pval == NULL) + /* don't update end here, leave at value start */ + return JSON_ERR_NULLPTR; + if (*cp == '\\') + state = in_escape; + else if (*cp == '"') { + *pval++ = '\0'; + json_debug_trace((1, "Collected string value %s\n", valbuf)); + state = post_val; + } else if (pval > valbuf + JSON_VAL_MAX - 1 + || pval > valbuf + maxlen) { + json_debug_trace((1, "String value too long.\n")); + /* don't update end here, leave at value start */ + return JSON_ERR_STRLONG; /* */ + } else + *pval++ = *cp; + break; + case in_escape: + if (pval == NULL) + /* don't update end here, leave at value start */ + return JSON_ERR_NULLPTR; + else if (pval > valbuf + JSON_VAL_MAX - 1 + || pval > valbuf + maxlen) { + json_debug_trace((1, "String value too long.\n")); + /* don't update end here, leave at value start */ + return JSON_ERR_STRLONG; /* */ + } + switch (*cp) { + case 'b': + *pval++ = '\b'; + break; + case 'f': + *pval++ = '\f'; + break; + case 'n': + *pval++ = '\n'; + break; + case 'r': + *pval++ = '\r'; + break; + case 't': + *pval++ = '\t'; + break; + case 'u': + cp++; /* skip the 'u' */ + for (n = 0; n < 4 && isxdigit(*cp); n++) + uescape[n] = *cp++; + uescape[n] = '\0'; /* terminate */ + --cp; + /* ECMA-404 says JSON \u must have 4 hex digits */ + if ((4 != n) || (1 != sscanf(uescape, "%4x", &u))) { + return JSON_ERR_BADSTRING; + } + *pval++ = (unsigned char)u; /* truncate values above 0xff */ + break; + default: /* handles double quote and solidus */ + *pval++ = *cp; + break; + } + state = in_val_string; + break; + case in_val_token: + if (pval == NULL) + /* don't update end here, leave at value start */ + return JSON_ERR_NULLPTR; + if (isspace((unsigned char) *cp) || *cp == ',' || *cp == '}') { + *pval = '\0'; + json_debug_trace((1, "Collected token value %s.\n", valbuf)); + state = post_val; + if (*cp == '}' || *cp == ',') + --cp; + } else if (pval > valbuf + JSON_VAL_MAX - 1) { + json_debug_trace((1, "Token value too long.\n")); + /* don't update end here, leave at value start */ + return JSON_ERR_TOKLONG; + } else + *pval++ = *cp; + break; + case post_val: + // Ignore whitespace after either string or token values. + if (isspace(*cp)) { + while (*cp != '\0' && isspace((unsigned char) *cp)) { + ++cp; + } + json_debug_trace((1, "Skipped trailing whitespace: value \"%s\"\n", valbuf)); + } + /* + * We know that cursor points at the first spec matching + * the current attribute. We don't know that it's *the* + * correct spec; our dialect allows there to be any number + * of adjacent ones with the same attrname but different + * types. Here's where we try to seek forward for a + * matching type/attr pair if we're not looking at one. + */ + for (;;) { + int seeking = cursor->type; + if (value_quoted && (cursor->type == t_string)) + break; +#ifdef TIME_ENABLE + if(value_quoted && (cursor->type == t_time)) + break; +#endif /* TIME_ENABLE */ + if ((strcmp(valbuf, "true")==0 || strcmp(valbuf, "false")==0 + || isdigit((unsigned char) valbuf[0])) + && seeking == t_boolean) + break; + if (isdigit((unsigned char) valbuf[0])) { + bool decimal = strchr(valbuf, '.') != NULL; + if (decimal && seeking == t_real) + break; + if (!decimal && (seeking == t_integer + || seeking == t_uinteger)) + break; + } + if (cursor[1].attribute==NULL) /* out of possiblities */ + break; + if (strcmp(cursor[1].attribute, attrbuf)!=0) + break; + ++cursor; + } + if (value_quoted + && (cursor->type != t_string && cursor->type != t_character + && cursor->type != t_check + && cursor->type != t_ignore && cursor->map == 0)) { + json_debug_trace((1, "Saw quoted value when expecting" + " non-string.\n")); +#ifdef TIME_ENABLE + if(value_quoted && cursor->type != t_time) + json_debug_trace((1, "Saw quoted value when expecting" + " non-string.\n")); +#endif /* TIME_ENABLE */ + return JSON_ERR_QNONSTRING; + } + if (!value_quoted + && (cursor->type == t_string || cursor->type == t_check || cursor->map != 0)) { + json_debug_trace((1, "Didn't see quoted value when expecting" + " string.\n")); +#ifdef TIME_ENABLE + if(!value_quoted && cursor->type == t_time) + json_debug_trace((1, "Didn't see quoted value when expecting" + " string.\n")); +#endif /* TIME_ENABLE */ + return JSON_ERR_NONQSTRING; + } + if (cursor->map != 0) { + for (mp = cursor->map; mp->name != NULL; mp++) + if (strcmp(mp->name, valbuf) == 0) { + goto foundit; + } + json_debug_trace((1, "Invalid enumerated value string \"%s\".\n", + valbuf)); + return JSON_ERR_BADENUM; + foundit: + (void)snprintf(valbuf, sizeof(valbuf), "%d", mp->value); + } + lptr = json_target_address(cursor, parent, offset); + if (lptr != NULL) + switch (cursor->type) { + case t_integer: + { + int tmp = atoi(valbuf); + memcpy(lptr, &tmp, sizeof(int)); + } + break; + case t_uinteger: + { + unsigned int tmp = (unsigned int)atoi(valbuf); + memcpy(lptr, &tmp, sizeof(unsigned int)); + } + break; + case t_short: + { + short tmp = atoi(valbuf); + memcpy(lptr, &tmp, sizeof(short)); + } + break; + case t_ushort: + { + unsigned short tmp = (unsigned int)atoi(valbuf); + memcpy(lptr, &tmp, sizeof(unsigned short)); + } + break; +#ifdef TIME_ENABLE + case t_time: + { + double tmp = iso8601_to_unix(valbuf); + memcpy(lptr, &tmp, sizeof(double)); + } +#endif /* TIME_ENABLE */ + break; + case t_real: + { + double tmp = atof(valbuf); + memcpy(lptr, &tmp, sizeof(double)); + } + break; + case t_string: + if (parent != NULL + && parent->element_type != t_structobject + && offset > 0) + return JSON_ERR_NOPARSTR; + else { + size_t vl = strlen(valbuf), cl = cursor->len-1; + memset(lptr, '\0', cl); + memcpy(lptr, valbuf, vl < cl ? vl : cl); + } + break; + case t_boolean: + { + bool tmp = (strcmp(valbuf, "true") == 0 || strtol(valbuf, NULL, 0)); + memcpy(lptr, &tmp, sizeof(bool)); + } + break; + case t_character: + if (strlen(valbuf) > 1) + /* don't update end here, leave at value start */ + return JSON_ERR_STRLONG; + else + lptr[0] = valbuf[0]; + break; + case t_ignore: /* silences a compiler warning */ + case t_object: /* silences a compiler warning */ + case t_structobject: + case t_array: + break; + case t_check: + if (strcmp(cursor->dflt.check, valbuf) != 0) { + json_debug_trace((1, "Required attribute value %s" + " not present.\n", + cursor->dflt.check)); + /* don't update end here, leave at start of attribute */ + return JSON_ERR_CHECKFAIL; + } + break; + } + __attribute__ ((fallthrough)); + case post_element: + if (isspace((unsigned char) *cp)) + continue; + else if (*cp == ',') + state = await_attr; + else if (*cp == '}') { + ++cp; + goto good_parse; + } else { + json_debug_trace((1, "Garbage while expecting comma or }\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_BADTRAIL; + } + break; + } + } + + good_parse: + /* in case there's another object following, consume trailing WS */ + while (*cp != '\0' && isspace((unsigned char) *cp)) + ++cp; + if (end != NULL) + *end = cp; + json_debug_trace((1, "JSON parse ends.\n")); + return 0; +} + +int json_read_array(const char *cp, const struct json_array_t *arr, + const char **end) +{ + int substatus, offset, arrcount; + char *tp; + + if (end != NULL) + *end = NULL; /* give it a well-defined value on parse failure */ + + json_debug_trace((1, "Entered json_read_array()\n")); + + while (isspace((unsigned char) *cp)) + cp++; + if (*cp != '[') { + json_debug_trace((1, "Didn't find expected array start\n")); + return JSON_ERR_ARRAYSTART; + } else + cp++; + + tp = arr->arr.strings.store; + arrcount = 0; + + /* Check for empty array */ + while (isspace((unsigned char) *cp)) + cp++; + if (*cp == ']') + goto breakout; + + for (offset = 0; offset < arr->maxlen; offset++) { + char *ep = NULL; + json_debug_trace((1, "Looking at %s\n", cp)); + switch (arr->element_type) { + case t_string: + if (isspace((unsigned char) *cp)) + cp++; + if (*cp != '"') + return JSON_ERR_BADSTRING; + else + ++cp; + arr->arr.strings.ptrs[offset] = tp; + for (; tp - arr->arr.strings.store < arr->arr.strings.storelen; + tp++) + if (*cp == '"') { + ++cp; + *tp++ = '\0'; + goto stringend; + } else if (*cp == '\0') { + json_debug_trace((1, + "Bad string syntax in string list.\n")); + return JSON_ERR_BADSTRING; + } else { + *tp = *cp++; + } + json_debug_trace((1, "Bad string syntax in string list.\n")); + return JSON_ERR_BADSTRING; + stringend: + break; + case t_object: + case t_structobject: + substatus = + json_internal_read_object(cp, arr->arr.objects.subtype, arr, + offset, &cp); + if (substatus != 0) { + if (end != NULL) + end = &cp; + return substatus; + } + break; + case t_integer: + arr->arr.integers.store[offset] = (int)strtol(cp, &ep, 0); + if (ep == cp) + return JSON_ERR_BADNUM; + else + cp = ep; + break; + case t_uinteger: + arr->arr.uintegers.store[offset] = (unsigned int)strtoul(cp, + &ep, 0); + if (ep == cp) + return JSON_ERR_BADNUM; + else + cp = ep; + break; + case t_short: + arr->arr.shorts.store[offset] = (short)strtol(cp, &ep, 0); + if (ep == cp) + return JSON_ERR_BADNUM; + else + cp = ep; + break; + case t_ushort: + arr->arr.ushorts.store[offset] = (unsigned short)strtol(cp, &ep, 0); + if (ep == cp) + return JSON_ERR_BADNUM; + else + cp = ep; + break; +#ifdef TIME_ENABLE + case t_time: + if (*cp != '"') + return JSON_ERR_BADSTRING; + else + ++cp; + arr->arr.reals.store[offset] = iso8601_to_unix((char *)cp); + if (arr->arr.reals.store[offset] >= HUGE_VAL) + return JSON_ERR_BADNUM; + while (*cp && *cp != '"') + cp++; + if (*cp != '"') + return JSON_ERR_BADSTRING; + else + ++cp; + break; +#endif /* TIME_ENABLE */ + case t_real: + arr->arr.reals.store[offset] = strtod(cp, &ep); + if (ep == cp) + return JSON_ERR_BADNUM; + else + cp = ep; + break; + case t_boolean: + if (str_starts_with(cp, "true")) { + arr->arr.booleans.store[offset] = true; + cp += 4; + } + else if (str_starts_with(cp, "false")) { + arr->arr.booleans.store[offset] = false; + cp += 5; + } else { + int val = strtol(cp, &ep, 0); + if (ep == cp) + return JSON_ERR_BADNUM; + else { + arr->arr.booleans.store[offset] = (bool) val; + cp = ep; + } + } + break; + case t_character: + case t_array: + case t_check: + case t_ignore: + json_debug_trace((1, "Invalid array subtype.\n")); + return JSON_ERR_SUBTYPE; + } + arrcount++; + if (isspace((unsigned char) *cp)) + cp++; + if (*cp == ']') { + json_debug_trace((1, "End of array found.\n")); + goto breakout; + } else if (*cp == ',') + cp++; + else { + json_debug_trace((1, "Bad trailing syntax on array.\n")); + return JSON_ERR_BADSUBTRAIL; + } + } + json_debug_trace((1, "Too many elements in array.\n")); + if (end != NULL) + *end = cp; + return JSON_ERR_SUBTOOLONG; + breakout: + if (arr->count != NULL) + *(arr->count) = arrcount; + if (end != NULL) + *end = cp; + json_debug_trace((1, "leaving json_read_array() with %d elements\n", + arrcount)); + return 0; +} + +int json_read_object(const char *cp, const struct json_attr_t *attrs, + const char **end) +{ + int st; + + json_debug_trace((1, "json_read_object() sees '%s'\n", cp)); + st = json_internal_read_object(cp, attrs, NULL, 0, end); + return st; +} + +const char *json_error_string(int err) +{ + const char *errors[] = { + "unknown error while parsing JSON", + "non-whitespace when expecting object start", + "non-whitespace when expecting attribute start", + "unknown attribute name", + "attribute name too long", + "saw [ when not expecting array", + "array element specified, but no [", + "string value too long", + "token value too long", + "garbage while expecting comma or } or ]", + "didn't find expected array start", + "error while parsing object array", + "too many array elements", + "garbage while expecting array comma", + "unsupported array element type", + "error while string parsing", + "check attribute not matched", + "can't support strings in parallel arrays", + "invalid enumerated value", + "saw quoted value when expecting nonstring", + "didn't see quoted value when expecting string", + "other data conversion error", + "unexpected null value or attribute pointer", + "object element specified, but no {", + }; + + if (err <= 0 || err >= (int)(sizeof(errors) / sizeof(errors[0]))) + return errors[0]; + else + return errors[err]; +} + +/* end */ + diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.h b/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.h new file mode 100644 index 0000000..40e3006 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/mjson.h @@ -0,0 +1,164 @@ +/* Structures for JSON parsing using only fixed-extent memory + * + * This file is Copyright (c) 2010 by the GPSD project + * SPDX-License-Identifier: BSD-2-clause + */ + +#ifndef MICROJSON_H_ +#define MICROJSON_H_ + +#include +#include +#include +#ifdef TIME_ENABLE +#include +#endif /* TIME_ENABLE */ + +typedef enum {t_integer, t_uinteger, t_real, + t_string, t_boolean, t_character, +#ifdef TIME_ENABLE + t_time, +#endif /* TIME_ENABLE */ + t_object, t_structobject, t_array, + t_check, t_ignore, + t_short, t_ushort} + json_type; + +struct json_enum_t { + char *name; + int value; +}; + +struct json_array_t { + json_type element_type; + union { + struct { + const struct json_attr_t *subtype; + char *base; + size_t stride; + } objects; + struct { + char **ptrs; + char *store; + int storelen; + } strings; + struct { + int *store; + } integers; + struct { + unsigned int *store; + } uintegers; + struct { + short *store; + } shorts; + struct { + unsigned short *store; + } ushorts; + struct { + double *store; + } reals; + struct { + bool *store; + } booleans; + } arr; + int *count, maxlen; +}; + +struct json_attr_t { + char *attribute; + json_type type; + union { + int *integer; + unsigned int *uinteger; + short *shortint; + unsigned short *ushortint; + double *real; + char *string; + bool *boolean; + char *character; + const struct json_attr_t *attrs; + const struct json_array_t array; + size_t offset; + } addr; + union { + int integer; + unsigned int uinteger; + short shortint; + unsigned short ushortint; + double real; + bool boolean; + char character; + char *check; + } dflt; + size_t len; + const struct json_enum_t *map; + bool nodefault; +}; + +#define JSON_ATTR_MAX 31 /* max chars in JSON attribute name */ +#define JSON_VAL_MAX 512 /* max chars in JSON value part */ + +#ifdef __cplusplus +extern "C" { +#endif +int json_read_object(const char *, const struct json_attr_t *, + const char **); +int json_read_array(const char *, const struct json_array_t *, + const char **); +const char *json_error_string(int); + +#ifdef TIME_ENABLE +extern time_t timegm(struct tm *tm); +#endif /* TIME_ENABLE */ + +void json_enable_debug(int, FILE *); +#ifdef __cplusplus +} +#endif + +#define JSON_ERR_OBSTART 1 /* non-WS when expecting object start */ +#define JSON_ERR_ATTRSTART 2 /* non-WS when expecting attrib start */ +#define JSON_ERR_BADATTR 3 /* unknown attribute name */ +#define JSON_ERR_ATTRLEN 4 /* attribute name too long */ +#define JSON_ERR_NOARRAY 5 /* saw [ when not expecting array */ +#define JSON_ERR_NOBRAK 6 /* array element specified, but no [ */ +#define JSON_ERR_STRLONG 7 /* string value too long */ +#define JSON_ERR_TOKLONG 8 /* token value too long */ +#define JSON_ERR_BADTRAIL 9 /* garbage while expecting comma or } or ] */ +#define JSON_ERR_ARRAYSTART 10 /* didn't find expected array start */ +#define JSON_ERR_OBJARR 11 /* error while parsing object array */ +#define JSON_ERR_SUBTOOLONG 12 /* too many array elements */ +#define JSON_ERR_BADSUBTRAIL 13 /* garbage while expecting array comma */ +#define JSON_ERR_SUBTYPE 14 /* unsupported array element type */ +#define JSON_ERR_BADSTRING 15 /* error while string parsing */ +#define JSON_ERR_CHECKFAIL 16 /* check attribute not matched */ +#define JSON_ERR_NOPARSTR 17 /* can't support strings in parallel arrays */ +#define JSON_ERR_BADENUM 18 /* invalid enumerated value */ +#define JSON_ERR_QNONSTRING 19 /* saw quoted value when expecting nonstring */ +#define JSON_ERR_NONQSTRING 19 /* didn't see quoted value when expecting string */ +#define JSON_ERR_MISC 20 /* other data conversion error */ +#define JSON_ERR_BADNUM 21 /* error while parsing a numerical argument */ +#define JSON_ERR_NULLPTR 22 /* unexpected null value or attribute pointer */ +#define JSON_ERR_NOCURLY 23 /* object element specified, but no { */ + +/* + * Use the following macros to declare template initializers for structobject + * arrays. Writing the equivalents out by hand is error-prone. + * + * STRUCTOBJECT takes a structure name s, and a fieldname f in s. + * + * STRUCTARRAY takes the name of a structure array, a pointer to a an + * initializer defining the subobject type, and the address of an integer to + * store the length in. + */ +#define STRUCTOBJECT(s, f) .addr.offset = offsetof(s, f) +#define STRUCTARRAY(a, e, n) \ + .addr.array.element_type = t_structobject, \ + .addr.array.arr.objects.subtype = e, \ + .addr.array.arr.objects.base = (char*)a, \ + .addr.array.arr.objects.stride = sizeof(a[0]), \ + .addr.array.count = n, \ + .addr.array.maxlen = (int)(sizeof(a)/sizeof(a[0])) + +/* json.h ends here */ +#endif /* MICROJSON_H_ */ diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/test_microjson.c b/ADIS_ESP32_Eclipse/lib/microjson-1.6/test_microjson.c new file mode 100644 index 0000000..a019b7e --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/test_microjson.c @@ -0,0 +1,978 @@ +/* test_mjson.c - unit test for JSON parsing into fixed-extent structures + * + * This file is Copyright (c) 2014 by Eric S. Raymond + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mjson.h" + +/* + * Many of these structures and examples were dissected out of the GPSD code. + */ + +#define MAXCHANNELS 20 +#define MAXUSERDEVS 4 +#define JSON_DATE_MAX 24 /* ISO8601 timestamp with 2 decimal places */ + +/* these values don't matter in themselves, they just have to be out-of-band */ +#define DEVDEFAULT_BPS 0 +#define DEVDEFAULT_PARITY 'X' +#define DEVDEFAULT_STOPBITS 3 +#define DEVDEFAULT_NATIVE -1 + +typedef double timestamp_t; /* Unix time in seconds with fractional part */ + +struct dop_t { + /* Dilution of precision factors */ + double xdop, ydop, pdop, hdop, vdop, tdop, gdop; +}; + +struct version_t { + char release[64]; /* external version */ + char rev[64]; /* internal revision ID */ + int proto_major, proto_minor; /* API major and minor versions */ + char remote[PATH_MAX]; /* could be from a remote device */ +}; + +struct devconfig_t { + char path[PATH_MAX]; + int flags; +#define SEEN_GPS 0x01 +#define SEEN_RTCM2 0x02 +#define SEEN_RTCM3 0x04 +#define SEEN_AIS 0x08 + char driver[64]; + char subtype[64]; + double activated; + unsigned int baudrate, stopbits; /* RS232 link parameters */ + char parity; /* 'N', 'O', or 'E' */ + double cycle, mincycle; /* refresh cycle time in seconds */ + int driver_mode; /* is driver in native mode or not? */ +}; +struct gps_fix_t { + timestamp_t time; /* Time of update */ + int mode; /* Mode of fix */ +#define MODE_NOT_SEEN 0 /* mode update not seen yet */ +#define MODE_NO_FIX 1 /* none */ +#define MODE_2D 2 /* good for latitude/longitude */ +#define MODE_3D 3 /* good for altitude/climb too */ + double ept; /* Expected time uncertainty */ + double latitude; /* Latitude in degrees (valid if mode >= 2) */ + double epy; /* Latitude position uncertainty, meters */ + double longitude; /* Longitude in degrees (valid if mode >= 2) */ + double epx; /* Longitude position uncertainty, meters */ + double altitude; /* Altitude in meters (valid if mode == 3) */ + double epv; /* Vertical position uncertainty, meters */ + double track; /* Course made good (relative to true north) */ + double epd; /* Track uncertainty, degrees */ + double speed; /* Speed over ground, meters/sec */ + double eps; /* Speed uncertainty, meters/sec */ + double climb; /* Vertical speed, meters/sec */ + double epc; /* Vertical speed uncertainty */ +}; + +struct gps_data_t { + struct gps_fix_t fix; /* accumulated PVT data */ + + /* this should move to the per-driver structure */ + double separation; /* Geoidal separation, MSL - WGS84 (Meters) */ + + /* GPS status -- always valid */ + int status; /* Do we have a fix? */ +#define STATUS_NO_FIX 0 /* no */ +#define STATUS_FIX 1 /* yes, without DGPS */ +#define STATUS_DGPS_FIX 2 /* yes, with DGPS */ + + /* precision of fix -- valid if satellites_used > 0 */ + int satellites_used; /* Number of satellites used in solution */ + int used[MAXCHANNELS]; /* PRNs of satellites used in solution */ + struct dop_t dop; + + /* redundant with the estimate elements in the fix structure */ + double epe; /* spherical position error, 95% confidence (meters) */ + + /* satellite status -- valid when satellites_visible > 0 */ + timestamp_t skyview_time; /* skyview timestamp */ + int satellites_visible; /* # of satellites in view */ + int PRN[MAXCHANNELS]; /* PRNs of satellite */ + int elevation[MAXCHANNELS]; /* elevation of satellite */ + int azimuth[MAXCHANNELS]; /* azimuth */ + double ss[MAXCHANNELS]; /* signal-to-noise ratio (dB) */ + + struct devconfig_t dev; /* device that shipped last update */ + + struct { + timestamp_t time; + int ndevices; + struct devconfig_t list[MAXUSERDEVS]; + } devices; + + struct version_t version; +}; + +static struct gps_data_t gpsdata; + +static int json_tpv_read(const char *buf, struct gps_data_t *gpsdata, + const char **endptr) +{ + const struct json_attr_t json_attrs_1[] = { + /* *INDENT-OFF* */ + {"class", t_check, .dflt.check = "TPV"}, + {"device", t_string, .addr.string = gpsdata->dev.path, + .len = sizeof(gpsdata->dev.path)}, +#ifdef TIME_ENABLE + {"time", t_time, .addr.real = &gpsdata->fix.time, + .dflt.real = NAN}, +#else + {"time", t_ignore}, +#endif /* TIME_ENABLE */ + {"ept", t_real, .addr.real = &gpsdata->fix.ept, + .dflt.real = NAN}, + {"lon", t_real, .addr.real = &gpsdata->fix.longitude, + .dflt.real = NAN}, + {"lat", t_real, .addr.real = &gpsdata->fix.latitude, + .dflt.real = NAN}, + {"alt", t_real, .addr.real = &gpsdata->fix.altitude, + .dflt.real = NAN}, + {"epx", t_real, .addr.real = &gpsdata->fix.epx, + .dflt.real = NAN}, + {"epy", t_real, .addr.real = &gpsdata->fix.epy, + .dflt.real = NAN}, + {"epv", t_real, .addr.real = &gpsdata->fix.epv, + .dflt.real = NAN}, + {"track", t_real, .addr.real = &gpsdata->fix.track, + .dflt.real = NAN}, + {"speed", t_real, .addr.real = &gpsdata->fix.speed, + .dflt.real = NAN}, + {"climb", t_real, .addr.real = &gpsdata->fix.climb, + .dflt.real = NAN}, + {"epd", t_real, .addr.real = &gpsdata->fix.epd, + .dflt.real = NAN}, + {"eps", t_real, .addr.real = &gpsdata->fix.eps, + .dflt.real = NAN}, + {"epc", t_real, .addr.real = &gpsdata->fix.epc, + .dflt.real = NAN}, + {"mode", t_integer, .addr.integer = &gpsdata->fix.mode, + .dflt.integer = MODE_NOT_SEEN}, + {NULL}, + /* *INDENT-ON* */ + }; + + return json_read_object(buf, json_attrs_1, endptr); +} + +static int json_sky_read(const char *buf, struct gps_data_t *gpsdata, + const char **endptr) +{ + bool usedflags[MAXCHANNELS]; + const struct json_attr_t json_attrs_2_1[] = { + /* *INDENT-OFF* */ + {"PRN", t_integer, .addr.integer = gpsdata->PRN}, + {"el", t_integer, .addr.integer = gpsdata->elevation}, + {"az", t_integer, .addr.integer = gpsdata->azimuth}, + {"ss", t_real, .addr.real = gpsdata->ss}, + {"used", t_boolean, .addr.boolean = usedflags}, + /* *INDENT-ON* */ + {NULL}, + }; + const struct json_attr_t json_attrs_2[] = { + /* *INDENT-OFF* */ + {"class", t_check, .dflt.check = "SKY"}, + {"device", t_string, .addr.string = gpsdata->dev.path, + .len = sizeof(gpsdata->dev.path)}, + {"hdop", t_real, .addr.real = &gpsdata->dop.hdop, + .dflt.real = NAN}, + {"xdop", t_real, .addr.real = &gpsdata->dop.xdop, + .dflt.real = NAN}, + {"ydop", t_real, .addr.real = &gpsdata->dop.ydop, + .dflt.real = NAN}, + {"vdop", t_real, .addr.real = &gpsdata->dop.vdop, + .dflt.real = NAN}, + {"tdop", t_real, .addr.real = &gpsdata->dop.tdop, + .dflt.real = NAN}, + {"pdop", t_real, .addr.real = &gpsdata->dop.pdop, + .dflt.real = NAN}, + {"gdop", t_real, .addr.real = &gpsdata->dop.gdop, + .dflt.real = NAN}, + {"satellites", t_array, .addr.array.element_type = t_object, + .addr.array.arr.objects.subtype=json_attrs_2_1, + .addr.array.maxlen = MAXCHANNELS, + .addr.array.count = &gpsdata->satellites_visible}, + {NULL}, + /* *INDENT-ON* */ + }; + int status, i, j; + + for (i = 0; i < MAXCHANNELS; i++) { + gpsdata->PRN[i] = 0; + usedflags[i] = false; + } + + status = json_read_object(buf, json_attrs_2, endptr); + if (status != 0) + return status; + + gpsdata->satellites_used = 0; + gpsdata->satellites_visible = 0; + (void)memset(gpsdata->used, '\0', sizeof(gpsdata->used)); + for (i = j = 0; i < MAXCHANNELS; i++) { + if(gpsdata->PRN[i] > 0) + gpsdata->satellites_visible++; + if (usedflags[i]) { + gpsdata->used[j++] = gpsdata->PRN[i]; + gpsdata->satellites_used++; + } + } + + return 0; +} + +static int json_devicelist_read(const char *buf, struct gps_data_t *gpsdata, + const char **endptr) +{ + const struct json_attr_t json_attrs_subdevices[] = { + /* *INDENT-OFF* */ + {"class", t_check, .dflt.check = "DEVICE"}, + {"path", t_string, STRUCTOBJECT(struct devconfig_t, path), + .len = sizeof(gpsdata->devices.list[0].path)}, + {"activated", t_real, STRUCTOBJECT(struct devconfig_t, activated)}, + {"flags", t_integer, STRUCTOBJECT(struct devconfig_t, flags)}, + {"driver", t_string, STRUCTOBJECT(struct devconfig_t, driver), + .len = sizeof(gpsdata->devices.list[0].driver)}, + {"subtype", t_string, STRUCTOBJECT(struct devconfig_t, subtype), + .len = sizeof(gpsdata->devices.list[0].subtype)}, + {"native", t_integer, STRUCTOBJECT(struct devconfig_t, driver_mode), + .dflt.integer = -1}, + {"bps", t_uinteger, STRUCTOBJECT(struct devconfig_t, baudrate), + .dflt.uinteger = DEVDEFAULT_BPS}, + {"parity", t_character, STRUCTOBJECT(struct devconfig_t, parity), + .dflt.character = DEVDEFAULT_PARITY}, + {"stopbits", t_uinteger, STRUCTOBJECT(struct devconfig_t, stopbits), + .dflt.integer = DEVDEFAULT_STOPBITS}, + {"cycle", t_real, STRUCTOBJECT(struct devconfig_t, cycle), + .dflt.real = NAN}, + {"mincycle", t_real, STRUCTOBJECT(struct devconfig_t, mincycle), + .dflt.real = NAN}, + {NULL}, + /* *INDENT-ON* */ + }; + const struct json_attr_t json_attrs_devices[] = { + {"class", t_check,.dflt.check = "DEVICES"}, + {"devices", t_array, STRUCTARRAY(gpsdata->devices.list, + json_attrs_subdevices, + &gpsdata->devices.ndevices)}, + {NULL}, + }; + int status; + + memset(&gpsdata->devices, '\0', sizeof(gpsdata->devices)); + status = json_read_object(buf, json_attrs_devices, endptr); + if (status != 0) { + return status; + } + return 0; +} + +static int json_device_read(const char *buf, + struct devconfig_t *dev, + const char **endptr) +{ + char tbuf[JSON_DATE_MAX+1]; + const struct json_attr_t json_attrs_device[] = { + {"class", t_check, .dflt.check = "DEVICE"}, + + {"path", t_string, .addr.string = dev->path, + .len = sizeof(dev->path)}, + {"activated", t_string, .addr.string = tbuf, + .len = sizeof(tbuf)}, + {"activated", t_real, .addr.real = &dev->activated}, + {"flags", t_integer, .addr.integer = &dev->flags}, + {"driver", t_string, .addr.string = dev->driver, + .len = sizeof(dev->driver)}, + {"subtype", t_string, .addr.string = dev->subtype, + .len = sizeof(dev->subtype)}, + {"native", t_integer, .addr.integer = &dev->driver_mode, + .dflt.integer = DEVDEFAULT_NATIVE}, + {"bps", t_uinteger, .addr.uinteger = &dev->baudrate, + .dflt.uinteger = DEVDEFAULT_BPS}, + {"parity", t_character, .addr.character = &dev->parity, + .dflt.character = DEVDEFAULT_PARITY}, + {"stopbits", t_uinteger, .addr.uinteger = &dev->stopbits, + .dflt.uinteger = DEVDEFAULT_STOPBITS}, + {"cycle", t_real, .addr.real = &dev->cycle, + .dflt.real = NAN}, + {"mincycle", t_real, .addr.real = &dev->mincycle, + .dflt.real = NAN}, + {NULL}, + }; + /* *INDENT-ON* */ + int status; + + tbuf[0] = '\0'; + status = json_read_object(buf, json_attrs_device, endptr); + if (status != 0) + return status; + + return 0; +} + +static int json_version_read(const char *buf, struct gps_data_t *gpsdata, + const char **endptr) +{ + const struct json_attr_t json_attrs_version[] = { + /* *INDENT-OFF* */ + {"class", t_check, .dflt.check = "VERSION"}, + {"release", t_string, .addr.string = gpsdata->version.release, + .len = sizeof(gpsdata->version.release)}, + {"rev", t_string, .addr.string = gpsdata->version.rev, + .len = sizeof(gpsdata->version.rev)}, + {"proto_major", t_integer, .addr.integer = &gpsdata->version.proto_major}, + {"proto_minor", t_integer, .addr.integer = &gpsdata->version.proto_minor}, + {"remote", t_string, .addr.string = gpsdata->version.remote, + .len = sizeof(gpsdata->version.remote)}, + {NULL}, + /* *INDENT-ON* */ + }; + int status; + + memset(&gpsdata->version, '\0', sizeof(gpsdata->version)); + status = json_read_object(buf, json_attrs_version, endptr); + + return status; +} + +static int libgps_json_unpack(const char *buf, + struct gps_data_t *gpsdata, const char **end) +/* the only entry point - unpack a JSON object into gpsdata_t substructures */ +{ + int status; + char *classtag = strstr(buf, "\"class\":"); + + if (classtag == NULL) + return -1; +#define STARTSWITH(str, prefix) strncmp(str, prefix, sizeof(prefix)-1)==0 + if (STARTSWITH(classtag, "\"class\":\"TPV\"")) { + status = json_tpv_read(buf, gpsdata, end); + gpsdata->status = STATUS_FIX; + return status; + } else if (STARTSWITH(classtag, "\"class\":\"SKY\"")) { + status = json_sky_read(buf, gpsdata, end); + return status; + } else if (STARTSWITH(classtag, "\"class\":\"DEVICE\"")) { + status = json_device_read(buf, &gpsdata->dev, end); + return status; + } else if (STARTSWITH(classtag, "\"class\":\"DEVICES\"")) { + status = json_devicelist_read(buf, gpsdata, end); + return status; + } else if (STARTSWITH(classtag, "\"class\":\"VERSION\"")) { + status = json_version_read(buf, gpsdata, end); + return status; + } +#undef STARTSWITH +} + +static int assert_error_case(int num, int status, int error) +{ + if (status != error) { + (void)fprintf(stderr, "case %d FAILED, status %d (%s).\n", num, + status, json_error_string(status)); + exit(EXIT_FAILURE); + } + else + return 0; +} + +static void assert_case(int num, int status) +{ + assert_error_case(num, status, 0); +} + +static void assert_string(char *attr, char *fld, char *check) +{ + if (strcmp(fld, check)) { + (void)fprintf(stderr, + "'%s' expecting string '%s', got '%s'.\n", + attr, check, fld); + exit(EXIT_FAILURE); + } +} + +static void assert_integer(char *attr, int fld, int check) +{ + if (fld != check) { + (void)fprintf(stderr, + "'%s' expecting integer %d, got %d.\n", + attr, check, fld); + exit(EXIT_FAILURE); + } +} + +static void assert_uinteger(char *attr, unsigned int fld, unsigned int check) +{ + if (fld != check) { + (void)fprintf(stderr, + "'%s' expecting uinteger %u, got %u.\n", + attr, check, fld); + exit(EXIT_FAILURE); + } +} + +static void assert_boolean(char *attr, bool fld, bool check) +{ + if (fld != check) { + (void)fprintf(stderr, + "'%s' expecting boolean %s, got %s.\n", + attr, + check ? "true" : "false", + fld ? "true" : "false"); + exit(EXIT_FAILURE); + } +} + +/* + * Floating point comparisons are iffy, but at least if any of these fail + * the output will make it clear whether it was a precision issue + */ +static void assert_real(char *attr, double fld, double check) +{ + if (fld != check) { + (void)fprintf(stderr, + "'%s' expecting real %f got %f.\n", + attr, check, fld); + exit(EXIT_FAILURE); + } +} + +/* Case 1: TPV report */ + +/* *INDENT-OFF* */ +static const char json_str1[] = "{\"class\":\"TPV\",\ + \"device\":\"GPS#1\", \ + \"time\":\"2005-06-19T12:12:42.03Z\", \ + \"lon\":46.498203637,\"lat\":7.568074350, \ + \"alt\":1327.780,\"epx\":21.000,\"epy\":23.000,\"epv\":124.484,\"mode\":3}"; + +/* Case 2: SKY report */ + +static const char *json_str2 = "{\"class\":\"SKY\",\ + \"satellites\":[\ + {\"PRN\":10,\"el\":45,\"az\":196,\"ss\":34,\"used\":true},\ + {\"PRN\":29,\"el\":67,\"az\":310,\"ss\":40,\"used\":true},\ + {\"PRN\":28,\"el\":59,\"az\":108,\"ss\":42,\"used\":true},\ + {\"PRN\":26,\"el\":51,\"az\":304,\"ss\":43,\"used\":true},\ + {\"PRN\":8,\"el\":44,\"az\":58,\"ss\":41,\"used\":true},\ + {\"PRN\":27,\"el\":16,\"az\":66,\"ss\":39,\"used\":true},\ + {\"PRN\":21,\"el\":10,\"az\":301,\"ss\":0,\"used\":false}]}"; + +/* Case 3: String list syntax */ + +static const char *json_str3 = "[\"foo\",\"bar\",\"baz\"]"; + +static char *stringptrs[3]; +static char stringstore[256]; +static int stringcount; + +static const struct json_array_t json_array_3 = { + .element_type = t_string, + .arr.strings.ptrs = stringptrs, + .arr.strings.store = stringstore, + .arr.strings.storelen = sizeof(stringstore), + .count = &stringcount, + .maxlen = sizeof(stringptrs)/sizeof(stringptrs[0]), +}; + +/* Case 4: test defaulting of unspecified attributes */ + +static const char *json_str4 = "{\"flag1\":true,\"flag2\":false}"; + +static bool flag1, flag2; +static double dftreal; +static int dftinteger; +static unsigned int dftuinteger; + +static const struct json_attr_t json_attrs_4[] = { + {"dftint", t_integer, .addr.integer = &dftinteger, .dflt.integer = -5}, + {"dftuint", t_integer, .addr.uinteger = &dftuinteger, .dflt.uinteger = 10}, + {"dftreal", t_real, .addr.real = &dftreal, .dflt.real = 23.17}, + {"flag1", t_boolean, .addr.boolean = &flag1,}, + {"flag2", t_boolean, .addr.boolean = &flag2,}, + {NULL}, +}; + +/* Case 5: test DEVICE parsing */ + +static const char *json_str5 = "{\"class\":\"DEVICE\",\ + \"path\":\"/dev/ttyUSB0\",\ + \"flags\":5,\ + \"driver\":\"Foonly\",\"subtype\":\"Foonly Frob\"\ + }"; + +/* Case 6: test parsing of subobject list into array of structures */ + +static const char *json_str6 = "{\"parts\":[\ + {\"name\":\"Urgle\", \"flag\":true, \"count\":3},\ + {\"name\":\"Burgle\",\"flag\":false,\"count\":1},\ + {\"name\":\"Witter\",\"flag\":true, \"count\":4},\ + {\"name\":\"Thud\", \"flag\":false,\"count\":1}]}"; + +struct dumbstruct_t { + char name[64]; + bool flag; + int count; +}; +static struct dumbstruct_t dumbstruck[5]; +static int dumbcount; + +static const struct json_attr_t json_attrs_6_subtype[] = { + {"name", t_string, .addr.offset = offsetof(struct dumbstruct_t, name), + .len = 64}, + {"flag", t_boolean, .addr.offset = offsetof(struct dumbstruct_t, flag),}, + {"count", t_integer, .addr.offset = offsetof(struct dumbstruct_t, count),}, + {NULL}, +}; + +static const struct json_attr_t json_attrs_6[] = { + {"parts", t_array, .addr.array.element_type = t_structobject, + .addr.array.arr.objects.base = (char*)&dumbstruck, + .addr.array.arr.objects.stride = sizeof(struct dumbstruct_t), + .addr.array.arr.objects.subtype = json_attrs_6_subtype, + .addr.array.count = &dumbcount, + .addr.array.maxlen = sizeof(dumbstruck)/sizeof(dumbstruck[0])}, + {NULL}, +}; + +/* Case 7: test parsing of version response */ + +static const char *json_str7 = "{\"class\":\"VERSION\",\ + \"release\":\"2.40dev\",\"rev\":\"dummy-revision\",\ + \"proto_major\":3,\"proto_minor\":1}"; + +/* Case 8: test parsing arrays of enumerated types */ + +static const char *json_str8 = "{\"fee\":\"FOO\",\"fie\":\"BAR\",\"foe\":\"BAZ\"}"; +static const struct json_enum_t enum_table[] = { + {"BAR", 6}, {"FOO", 3}, {"BAZ", 14}, {NULL} +}; + +static int fee, fie, foe; +static const struct json_attr_t json_attrs_8[] = { + {"fee", t_integer, .addr.integer = &fee, .map=enum_table}, + {"fie", t_integer, .addr.integer = &fie, .map=enum_table}, + {"foe", t_integer, .addr.integer = &foe, .map=enum_table}, + {NULL}, +}; + +/* Case 9: Like case 6 but w/ an empty array */ + +static const char *json_str9 = "{\"parts\":[]}"; + +/* Case 10: test buffer overflow of short string destination */ + +// static char json_strErr2[7 * JSON_VAL_MAX]; /* dynamically built */ +static char *json_strOver = "{\"name\":\"\\u0033\\u0034\\u0035\\u0036\"}"; + +char json_short_string_dst[1]; +static const struct json_attr_t json_short_string[] = { + {"name", t_string, + .addr.string = json_short_string_dst, + .len = sizeof(json_short_string_dst)}, + {NULL}, +}; + +/* Case 11: Read array of integers */ + +static const char *json_str11 = "[23,-17,5]"; +static int intstore[4], intcount; + +static const struct json_array_t json_array_11 = { + .element_type = t_integer, + .arr.integers.store = intstore, + .count = &intcount, + .maxlen = sizeof(intstore)/sizeof(intstore[0]), +}; + +/* Case 12: Read array of booleans */ + +static const char *json_str12 = "[true,false,true]"; +static bool boolstore[4]; +static int boolcount; + +static const struct json_array_t json_array_12 = { + .element_type = t_boolean, + .arr.booleans.store = boolstore, + .count = &boolcount, + .maxlen = sizeof(boolstore)/sizeof(boolstore[0]), +}; + +/* Case 13: Read array of reals */ + +static const char *json_str13 = "[23.1,-17.2,5.3]"; +static double realstore[4]; +static int realcount; + +static const struct json_array_t json_array_13 = { + .element_type = t_real, + .arr.reals.store = realstore, + .count = &realcount, + .maxlen = sizeof(realstore)/sizeof(realstore[0]), +}; + +/* Case 14: Read object within object. Test case not derived from GPSD */ + +static char json_inner_name_string_dst[JSON_VAL_MAX]; +static int inner_value; +static const char *json_str14 = "{\"name\":\"wobble\",\"value\":{\"inner\":23}}"; +const struct json_attr_t json_inner_int_value[] = { + {"inner", t_integer, + .addr.integer = &inner_value}, + {NULL}, +}; +static const struct json_attr_t json_object_14[] = { + {"name", t_string, + .addr.string = json_inner_name_string_dst, + .len = sizeof(json_inner_name_string_dst)}, + {"value", t_object, + .addr.attrs = json_inner_int_value}, + {NULL}, +}; + +/* Case 15: test parsing 0/1 integer values as bool */ + +static bool flag3; +static bool flags4[3]; + +static const char *json_str15 = "{\"flag1\":1, \"flag2\":0, \"flag3\":7, \"flags4\":[1,0,7]}"; + +static const struct json_attr_t json_attrs_15[] = { + {"flag1", t_boolean, .addr.boolean = &flag1,}, + {"flag2", t_boolean, .addr.boolean = &flag2,}, + {"flag3", t_boolean, .addr.boolean = &flag3,}, + {"flags4", t_array, .addr.array.element_type = t_boolean, + .addr.array.arr.booleans = flags4, + .addr.array.maxlen = 3,}, + {NULL}, +}; + +/* Case 16: Read object within object within object.*/ + +char json_inner1_name_string_dst[JSON_VAL_MAX]; + +int inner1_value; +int inner2_value; + +static const char *json_str16 = "{\"name\":\"wobble\",\"value\":{\"inner\":23, \"innerobject\":{\"innerinner\":123}}}\0READ PAST END OF BUFFER"; + +const struct json_attr_t json_inner2_int_value[] = { + {"innerinner", t_integer, + .addr.integer = &inner2_value}, + {NULL}, +}; +const struct json_attr_t json_inner1_int_value[] = { + {"inner", t_integer, + .addr.integer = &inner1_value}, + {"innerobject", t_object, + .addr.attrs = json_inner2_int_value}, + {NULL}, +}; +static const struct json_attr_t json_object_16[] = { + {"name", t_string, + .addr.string = json_inner1_name_string_dst, + .len = sizeof(json_inner1_name_string_dst)}, + {"value", t_object, + .addr.attrs = json_inner1_int_value}, + {NULL}, +}; + +/* Case 17: Parent struct enclosing enum in sub-struct followed by value outside of sub-struct */ + +static int eval, ival; +static const char *json_str17 = "{\"parentStruct\":{\"enumStruct\":{\"enumName\":\"NOT_SET\"\n},\"intName\":1}}"; +static const struct json_enum_t enum_table16[] = { + {"NOT_SET", 0}, {"SET", 1},{NULL} +}; + +static const struct json_attr_t json_enum_struct[] = { + {"enumName", t_integer,.addr.integer = &eval,.map = enum_table16}, + {NULL}, +}; +static const struct json_attr_t json_sub_struct[] = { + {"enumStruct", t_object,.addr.attrs = json_enum_struct}, + {"intName", t_integer,.addr.integer = &ival}, + {NULL}, +}; +static const struct json_attr_t json_object_17[] = { + {"parentStruct", t_object,.addr.attrs = json_sub_struct}, + {NULL}, +}; + +/* Case 18: Call json_read_object() on concatenated JSON objects. */ + +static const char *json_cur18; +static const char *json_end18; + +static const char *json_str18 = "{\"flag1\":1}{\"flag1\":0}{\"flag1\":7, \"flags4\":[1,0,7]} {\"flag2\":true, \"flags4\":[0,true,false]}"; + +/* Insert more test definitions here */ +/* *INDENT-ON* */ + +static void jsontest(int i) +{ + int status = 0; + + switch (i) + { + case 1: + status = libgps_json_unpack(json_str1, &gpsdata, NULL); + assert_case(i, status); + assert_string("device", gpsdata.dev.path, "GPS#1"); +#ifdef TIME_ENABLE + assert_real("time", gpsdata.fix.time, 1119183162.030000); +#endif /* TIME_ENABLE */ + assert_integer("mode", gpsdata.fix.mode, 3); + assert_real("lon", gpsdata.fix.longitude, 46.498203637); + assert_real("lat", gpsdata.fix.latitude, 7.568074350); + break; + + case 2: + status = libgps_json_unpack(json_str2, &gpsdata, NULL); + assert_case(i, status); + assert_integer("used", gpsdata.satellites_used, 6); + assert_integer("PRN[0]", gpsdata.PRN[0], 10); + assert_integer("el[0]", gpsdata.elevation[0], 45); + assert_integer("az[0]", gpsdata.azimuth[0], 196); + assert_real("ss[0]", gpsdata.ss[0], 34); + assert_integer("used[0]", gpsdata.used[0], 10); + assert_integer("used[5]", gpsdata.used[5], 27); + assert_integer("PRN[6]", gpsdata.PRN[6], 21); + assert_integer("el[6]", gpsdata.elevation[6], 10); + assert_integer("az[6]", gpsdata.azimuth[6], 301); + assert_real("ss[6]", gpsdata.ss[6], 0); + break; + + case 3: + status = json_read_array(json_str3, &json_array_3, NULL); + assert_case(i, status); + assert(stringcount == 3); + assert(strcmp(stringptrs[0], "foo") == 0); + assert(strcmp(stringptrs[1], "bar") == 0); + assert(strcmp(stringptrs[2], "baz") == 0); + break; + + case 4: + status = json_read_object(json_str4, json_attrs_4, NULL); + assert_case(i, status); + assert_integer("dftint", dftinteger, -5); /* did the default work? */ + assert_uinteger("dftuint", dftuinteger, 10); /* did the default work? */ + assert_real("dftreal", dftreal, 23.17); /* did the default work? */ + assert_boolean("flag1", flag1, true); + assert_boolean("flag2", flag2, false); + break; + + case 5: + status = libgps_json_unpack(json_str5, &gpsdata, NULL); + assert_case(i, status); + assert_string("path", gpsdata.dev.path, "/dev/ttyUSB0"); + assert_integer("flags", gpsdata.dev.flags, 5); + assert_string("driver", gpsdata.dev.driver, "Foonly"); + break; + + case 6: + status = json_read_object(json_str6, json_attrs_6, NULL); + assert_case(i, status); + assert_integer("dumbcount", dumbcount, 4); + assert_string("dumbstruck[0].name", dumbstruck[0].name, "Urgle"); + assert_string("dumbstruck[1].name", dumbstruck[1].name, "Burgle"); + assert_string("dumbstruck[2].name", dumbstruck[2].name, "Witter"); + assert_string("dumbstruck[3].name", dumbstruck[3].name, "Thud"); + assert_boolean("dumbstruck[0].flag", dumbstruck[0].flag, true); + assert_boolean("dumbstruck[1].flag", dumbstruck[1].flag, false); + assert_boolean("dumbstruck[2].flag", dumbstruck[2].flag, true); + assert_boolean("dumbstruck[3].flag", dumbstruck[3].flag, false); + assert_integer("dumbstruck[0].count", dumbstruck[0].count, 3); + assert_integer("dumbstruck[1].count", dumbstruck[1].count, 1); + assert_integer("dumbstruck[2].count", dumbstruck[2].count, 4); + assert_integer("dumbstruck[3].count", dumbstruck[3].count, 1); + break; + + case 7: + status = libgps_json_unpack(json_str7, &gpsdata, NULL); + assert_case(i, status); + assert_string("release", gpsdata.version.release, "2.40dev"); + assert_string("rev", gpsdata.version.rev, "dummy-revision"); + assert_integer("proto_major", gpsdata.version.proto_major, 3); + assert_integer("proto_minor", gpsdata.version.proto_minor, 1); + break; + + case 8: + status = json_read_object(json_str8, json_attrs_8, NULL); + assert_case(i, status); + assert_integer("fee", fee, 3); + assert_integer("fie", fie, 6); + assert_integer("foe", foe, 14); + break; + + case 9: + /* yes, the '6' in the next line is correct */ + status = json_read_object(json_str9, json_attrs_6, NULL); + assert_case(i, status); + assert_integer("dumbcount", dumbcount, 0); + break; + + case 10: + status = json_read_object(json_strOver, json_short_string, NULL); + status = assert_error_case(10, status, JSON_ERR_STRLONG); + assert_string("name", json_short_string_dst, ""); + break; + + case 11: + status = json_read_array(json_str11, &json_array_11, NULL); + assert_integer("count", intcount, 3); + assert_integer("intstore[0]", intstore[0], 23); + assert_integer("intstore[1]", intstore[1], -17); + assert_integer("intstore[2]", intstore[2], 5); + assert_integer("intstore[3]", intstore[3], 0); + break; + + case 12: + status = json_read_array(json_str12, &json_array_12, NULL); + assert_integer("count", boolcount, 3); + assert_boolean("boolstore[0]", boolstore[0], true); + assert_boolean("boolstore[1]", boolstore[1], false); + assert_boolean("boolstore[2]", boolstore[2], true); + assert_boolean("boolstore[3]", boolstore[3], false); + break; + + case 13: + status = json_read_array(json_str13, &json_array_13, NULL); + assert_integer("count", realcount, 3); + assert_real("realstore[0]", realstore[0], 23.1); + assert_real("realstore[1]", realstore[1], -17.2); + assert_real("realstore[2]", realstore[2], 5.3); + assert_real("realstore[3]", realstore[3], 0); + break; + + case 14: + status = json_read_object(json_str14, json_object_14, NULL); + assert_case(i, status); + assert_integer("inner", inner_value, 23); + assert_string("name", json_inner_name_string_dst, "wobble"); + break; + + case 15: + status = json_read_object(json_str15, json_attrs_15, NULL); + assert_case(i, status); + assert_boolean("flag1", flag1, true); + assert_boolean("flag2", flag2, false); + assert_boolean("flag3", flag3, true); + assert_boolean("flags4[0]", flags4[0], true); + assert_boolean("flags4[1]", flags4[1], false); + assert_boolean("flags4[2]", flags4[2], true); + break; + + case 16: + status = json_read_object(json_str16, json_object_16, NULL); + assert_case(i, status); + assert_integer("inner", inner1_value, 23); + assert_integer("innerinner", inner2_value, 123); + assert_string("name", json_inner1_name_string_dst, "wobble"); + break; + + case 17: + status = json_read_object(json_str17, json_object_17, NULL); + assert_case(i, status); + assert_integer("ival", ival, 1); + assert_integer("eval", eval, 0); + break; + + case 18: + json_cur18 = json_str18; + json_end18 = json_str18 + strlen(json_str18); + /* first JSON message */ + status = json_read_object(json_cur18, json_attrs_15, &json_cur18); + assert_case(i, status); + assert_boolean("flag1", flag1, true); + /* second JSON message */ + status = json_read_object(json_cur18, json_attrs_15, &json_cur18); + assert_case(i, status); + assert_boolean("flag1", flag1, false); + /* third JSON message */ + status = json_read_object(json_cur18, json_attrs_15, &json_cur18); + assert_case(i, status); + assert_boolean("flag1", flag1, true); + assert_boolean("flags4[0]", flags4[0], true); + assert_boolean("flags4[1]", flags4[1], false); + assert_boolean("flags4[2]", flags4[2], true); + /* fourth JSON message */ + status = json_read_object(json_cur18, json_attrs_15, &json_cur18); + assert_case(i, status); + assert_boolean("flag2", flag2, true); + assert_boolean("flags4[0]", flags4[0], false); + assert_boolean("flags4[1]", flags4[1], true); + assert_boolean("flags4[2]", flags4[2], false); + break; + +#define MAXTEST 18 + + default: + (void)fputs("Unknown test number\n", stderr); + break; + } + + if (status > 0) + printf("Parse failure!\n"); +} + +int main(int argc, char *argv[]) +{ + int option; + int individual = 0; + + while ((option = getopt(argc, argv, "hn:D:?")) != -1) { + switch (option) { + case 'D': +#ifdef DEBUG_ENABLE + json_enable_debug(atoi(optarg), stdout); +#else + fputs("Debug disabled in build\n", stderr); +#endif + break; + case 'n': + individual = atoi(optarg); + break; + case '?': + case 'h': + default: + (void)fputs("usage: test_json [-D lvl]\n", stderr); + exit(EXIT_FAILURE); + } + } + + (void)fprintf(stderr, "microjson unit test "); + + if (individual) + jsontest(individual); + else { + int i; + for (i = 1; i <= MAXTEST; i++) + jsontest(i); + } + + (void)fprintf(stderr, "succeeded.\n"); + + exit(EXIT_SUCCESS); +} + +/* end */ diff --git a/ADIS_ESP32_Eclipse/lib/microjson-1.6/test_microjson_wignore.c b/ADIS_ESP32_Eclipse/lib/microjson-1.6/test_microjson_wignore.c new file mode 100644 index 0000000..b9ebfd9 --- /dev/null +++ b/ADIS_ESP32_Eclipse/lib/microjson-1.6/test_microjson_wignore.c @@ -0,0 +1,117 @@ +//#include +#include +#include +#include +#include +#include +#include +#include "mjson.h" + +static int test_ver(void); +static int test_watch(void); +static int test_tpv(void); +int main(void); + +static char *VER = + "{\"class\":\"VERSION\",\"release\":\"3.19.1~dev\",\"rev\":\"release-3.19-" + "655-gb4aded4c1\",\"proto_major\":3,\"proto_minor\":14}"; +static char *WAT = + "{\"class\":\"WATCH\",\"enable\":true,\"json\":true,\"nmea\":false,\"raw\":" + "0,\"scaled\":false,\"timing\":false,\"split24\":false,\"pps\":false," + "\"device\":\"/dev/ttyUSB0\"}"; +static char *TPV = + "{\"class\":\"TPV\",\"device\":\"/dev/" + "ttyUSB0\",\"mode\":3,\"time\":\"2019-10-04T08:51:34.000Z\",\"ept\":0.005," + "\"lat\":46.367303831,\"lon\":-116.963791235,\"altHAE\":460.834,\"altMSL\":" + "476.140,\"epx\":7.842,\"epy\":12.231,\"epv\":30.607,\"track\":57.1020," + "\"magtrack\":70.9299,\"magvar\":13.8,\"speed\":0.065,\"climb\":-0.206," + "\"eps\":24.46,\"epc\":61.21,\"ecefx\":-1999242.00,\"ecefy\":-3929871.00," + "\"ecefz\":4593848.00,\"ecefvx\":0.12,\"ecefvy\":0.12,\"ecefvz\":-0.12," + "\"velN\":0.035,\"velE\":0.055,\"velD\":0.206,\"geoidSep\":-15.307,\"eph\":" + "15.200,\"sep\":31.273}"; + +#define icmp(want, got) \ + if (want != got) { \ + tally++; \ + printf("wanted %d got %d\n", want, got); \ + } +#define fcmp(want, got, tol) \ + if (fabs(want - got) > tol) { \ + tally++; \ + printf("wanted %f got %f diff %f > %f\n", want, got, (want-got), tol); \ + } +#define scmp(want, got) \ + if ((NULL == &got)||(0 != strcmp(want, got))) { \ + tally++; \ + printf("wanted '%s' got '%s'\n", want, got); \ + } +#define fin() icmp(0, errno) return tally; + +static int test_ver() { + int tally = 0; + char revision[50]; + uint16_t pvhi, pvlo; + const struct json_attr_t json_attrs_version[] = { + {"class", t_check, .dflt.check = "VERSION"}, + {"rev", t_string, .addr.string = (char *)&revision, .len = 50}, + {"proto_major", t_ushort, .addr.ushortint = &pvhi}, + {"proto_minor", t_ushort, .addr.ushortint = &pvlo}, + {"", t_ignore}, + {NULL}, + }; + printf("."); + errno = json_read_object(VER, json_attrs_version, NULL); + icmp(3, pvhi); + icmp(14, pvlo); + fin(); +} + +static int test_watch() { + int tally = 0; + bool enable, json; + const struct json_attr_t json_attrs_watch[] = { + {"class", t_check, .dflt.check = "WATCH"}, + {"device", t_check, .dflt.check = "/dev/ttyUSB0"}, + {"enable", t_boolean, .addr.boolean = &enable}, + {"json", t_boolean, .addr.boolean = &json}, + {"", t_ignore}, + {NULL}, + }; + printf("."); + errno = json_read_object(WAT, json_attrs_watch, NULL); + icmp(true, enable); + icmp(true, json); + fin(); +} + +static int test_tpv() { + int tally = 0; + int gps_mode; + double ept; + char gps_time[50]; + const struct json_attr_t json_attrs_tpv[] = { + {"class", t_check, .dflt.check = "TPV"}, + {"device", t_check, .dflt.check = "/dev/ttyUSB0"}, + {"mode", t_integer, .addr.integer = &gps_mode, .dflt.integer = -1}, + {"time", t_string, .addr.string = (char *)&gps_time, .len = 50}, + {"ept", t_real, .addr.real = &ept, .dflt.real = NAN}, + {"", t_ignore}, + {NULL}, + }; + printf("."); + errno = json_read_object(TPV, json_attrs_tpv, NULL); + icmp(3, gps_mode) fcmp(0.005, ept, 0.001); + scmp("2019-10-04T08:51:34.000Z", gps_time); + fin(); +} + +int main() { + int count = 0; + count += test_ver(); + count += test_watch(); + count += test_tpv(); + if (count) { + printf("OOPS: %d\n", count); + } + return count; +} diff --git a/ADIS_ESP32_Eclipse/main/CMakeLists.txt b/ADIS_ESP32_Eclipse/main/CMakeLists.txt index 4717782..dff554d 100644 --- a/ADIS_ESP32_Eclipse/main/CMakeLists.txt +++ b/ADIS_ESP32_Eclipse/main/CMakeLists.txt @@ -17,7 +17,6 @@ idf_component_register( "sntp_time.c" "robot.c" "myMqtt.c" - "application.c" "challenge_app.c" "splitflap_wrapper.c" "challenge_com.c" diff --git a/ADIS_ESP32_Eclipse/main/Shell.c b/ADIS_ESP32_Eclipse/main/Shell.c index c5e8b85..0fbb4dc 100644 --- a/ADIS_ESP32_Eclipse/main/Shell.c +++ b/ADIS_ESP32_Eclipse/main/Shell.c @@ -79,7 +79,7 @@ static const McuShell_ParseCommandCallback CmdParserTable[] = LED_ParseCommand, #endif #if PL_CONFIG_CHALLENGE_APP_ACTIVATED - Challenge_ParseCommand, + Challenge_ParseShellCommand, #endif NULL /* Sentinel */ }; diff --git a/ADIS_ESP32_Eclipse/main/challenge_app.c b/ADIS_ESP32_Eclipse/main/challenge_app.c index d9372fa..570fb6d 100644 --- a/ADIS_ESP32_Eclipse/main/challenge_app.c +++ b/ADIS_ESP32_Eclipse/main/challenge_app.c @@ -6,12 +6,15 @@ */ +#include #include "challenge_app.h" #include "platform.h" +#include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "myMqtt.h" #include "wifi.h" +#include "McuUtility.h" #include "challenge_com.h" /* LOCAL Var */ @@ -29,24 +32,23 @@ static void appTask(void *pv){ }*/ bool myMqttInitSuccess = false; - ESP_LOGI("Challenge application was started. Waiting for WiFi connection...."); + ESP_LOGI(TAG, "Challenge application was started. Waiting for WiFi connection...."); while(WiFi_isConnected() == false){ vTaskDelay(pdMS_TO_TICKS(1000)); } // wifi now connected - ESP_LOGI("Challenge Application task detected that WiFi is connected."); + ESP_LOGI(TAG, "Challenge Application task detected that WiFi is connected."); // endless loop: always reconnect to MQTT when something fails while(1){ - ESP_LOGI("MyMqtt Initializing...."); + ESP_LOGI(TAG, "MyMqtt Initializing...."); myMqttInitSuccess = MyMqtt_Init(); if(myMqttInitSuccess){ - ESP_LOGI("MyMqtt Initialization successful."); - ESP_LOGI("Challenge Application started."); + ESP_LOGI(TAG, "MyMqtt Initialization successful."); + ESP_LOGI(TAG, "Challenge Application started."); + + Challenge_Com_SubscribeToAllTopics(); - MyMqtt_Subscribe(MQTT_TOPIC_SF_INITALL); - MyMqtt_Subscribe(MQTT_TOPIC_SF_DISPLAY); - MyMqtt_Subscribe(MQTT_TOPIC_SF_CONFIG_SETUP); MyMqtt_Subscribe("scada/status"); // endless loop @@ -57,9 +59,10 @@ static void appTask(void *pv){ } } - ESP_LOGE(myMqttInitSuccess?"Something in challenge app failed. Re-initializing MQTT in 5s.":"Init of MyMqtt failed! Retrying in 5s."); + // TODO LOG + //ESP_LOGE(TAG, myMqttInitSuccess?(unsigned char*)"Something in challenge app failed. Re-initializing MQTT in 5s.":(unsigned char*)"Init of MyMqtt failed! Retrying in 5s."); MyMqtt_Deinit(); - if(myMqttInitSuccess == false) vTaskDelay(5000); // only wait if the init is the issue + if(myMqttInitSuccess == false) { vTaskDelay(5000); } // only wait if the init is the issue } } @@ -97,16 +100,15 @@ static uint8_t PrintHelp(const McuShell_StdIOType *io) { return ERR_OK; } -uint8_t Challenge_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { +uint8_t Challenge_ParseShellCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"challenge help")==0) { *handled = TRUE; return PrintHelp(io); } else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"challenge status")==0) { *handled = TRUE; return PrintStatus(io); - } - else if (McuUtility_strcmp((char*)cmd, (char*)"challenge setMode stationary")==0 || - McuUtility_strcmp((char*)cmd, (char*)"challenge setMode s")==0) { + } else if (McuUtility_strcmp((char*)cmd, (char*)"challenge setMode stationary")==0 || + McuUtility_strcmp((char*)cmd, (char*)"challenge setMode s")==0) { *handled = TRUE; Challenge_App_SetRobotMode(true); // set stationary } else if (McuUtility_strcmp((char*)cmd, (char*)"challenge setMode mobile")==0 || diff --git a/ADIS_ESP32_Eclipse/main/challenge_app.h b/ADIS_ESP32_Eclipse/main/challenge_app.h index b2d107b..e506bf7 100644 --- a/ADIS_ESP32_Eclipse/main/challenge_app.h +++ b/ADIS_ESP32_Eclipse/main/challenge_app.h @@ -8,14 +8,14 @@ #ifndef MAIN_CHALLENGE_APP_H_ #define MAIN_CHALLENGE_APP_H_ -#include "stdbool.h" +#include /*! \brief Module initialization, start app task */ void Challenge_App_Init(void); /*! \brief get mode of robot * \return true if stationary, false if mobile */ -bool Challenge_App_GetRobotMode(void){ +bool Challenge_App_GetRobotMode(void); #if PL_CONFIG_USE_SHELL @@ -28,7 +28,7 @@ bool Challenge_App_GetRobotMode(void){ * \param io I/O handler to be used * \return error code, otherwise ERR_OK */ - uint8_t Challenge_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); + uint8_t Challenge_ParseShellCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); #endif #endif /* MAIN_CHALLENGE_APP_H_ */ diff --git a/ADIS_ESP32_Eclipse/main/challenge_com.c b/ADIS_ESP32_Eclipse/main/challenge_com.c index cbd0888..381d257 100644 --- a/ADIS_ESP32_Eclipse/main/challenge_com.c +++ b/ADIS_ESP32_Eclipse/main/challenge_com.c @@ -5,12 +5,26 @@ * Author: jonas */ -#include "stdbool.h" +#include + +#include "challenge_com.h" #include "challenge_app.h" +#include "platform.h" +#include "esp_log.h" +#include "McuUtility.h" +#include "mjson.h" +#include "mqtt_client.h" +#include "myMqtt.h" +#include "splitflap_wrapper.h" #define TAG "CHALLENGE_COM" /* tag for logging with ESP_LOG */ -static void Challenge_Com_ParseMqtt(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data){ +/* TOPICS */ +const char MQTT_TOPIC_SF_DISPLAY[] = "/splitFlap/cmd/display/"; +const char MQTT_TOPIC_SF_INITALL[] = "/splitFlap/cmd/init/"; +const char MQTT_TOPIC_SF_CONFIG_SETUP[] = "/splitFlap/config/setup/"; + +void Challenge_Com_ParseMqtt(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data){ /* INFO * TOPIC: event->topic * TOPIC LENGTH: event->topic_len @@ -31,20 +45,47 @@ static void Challenge_Com_ParseMqtt(void *handler_args, esp_event_base_t base, i /* check topic and call related command if allowed by robot mode */ /* stationary robot allowed commands */ if(Challenge_App_GetRobotMode() == true) { + // CMD: CONFIG SETUP if(McuUtility_strcmp((char*)event->topic, MQTT_TOPIC_SF_CONFIG_SETUP)==0){ handled = true; - // TODO JSON PARSER - uint8_t setupId = 0; - uint8_t hwId = 1; - SplitFlap_Wrapper_SetHardwareIdentifier(setupId, hwId); - } else if(McuUtility_strcmp((char*)event->topic, MQTT_TOPIC_SF_INITALL)==0){ + // parse json + int setupId = 0; + int hwId = 0; + struct json_attr_t json_attrs[] = { + {"setupId", t_integer, .addr.integer = &setupId}, + {"hardwareId", t_integer, .addr.integer = &hwId,}, + {NULL}, + }; + if(json_read_object(event->data, json_attrs, NULL) != 0){ + ESP_LOGE(TAG, "Parsing JSON data of CONFIG SETUP message failed. Event data was = %s", event->data); + } else{ // successfully parsed + // check values + if(setupId >= 0 && setupId <= 10 && hwId >= 0 && hwId <= 10){ + SplitFlap_Wrapper_SetHardwareIdentifier(setupId, hwId); + } else{ + ESP_LOGE(TAG, "SetupID or HardwareID out of range. Event data was = %s", event->data); + } + } + } + // CMD: INIT ALL + else if(McuUtility_strcmp((char*)event->topic, MQTT_TOPIC_SF_INITALL)==0){ handled = true; SplitFlap_Wrapper_MoveAllToZeroPosition(); - } else if(McuUtility_strcmp((char*)event->topic, MQTT_TOPIC_SF_DISPLAY)==0){ + } + // CMD: DISPLAY + else if(McuUtility_strcmp((char*)event->topic, MQTT_TOPIC_SF_DISPLAY)==0){ handled = true; - // TODO JSON PARSER + // parse json char message[] = "TST"; - SplitFlap_Wrapper_Display(message); + struct json_attr_t json_attrs[] = { + {"message", t_string, .addr.string = message}, + {NULL}, + }; + if(json_read_object(event->data, json_attrs, NULL) != 0){ + ESP_LOGE(TAG, "Parsing JSON data of DISPLAY message failed. Event data was = %s", event->data); + } else{ // successfully parsed + SplitFlap_Wrapper_Display(message); + } } } /* mobile robot allowed commands */ @@ -56,11 +97,14 @@ static void Challenge_Com_ParseMqtt(void *handler_args, esp_event_base_t base, i - if(handled == false) - { + if(handled == false){ ESP_LOGE(TAG, "Received data could not be handled. Topic was %s", event->topic); } - return; } +void Challenge_Com_SubscribeToAllTopics(void){ + MyMqtt_Subscribe(MQTT_TOPIC_SF_INITALL); + MyMqtt_Subscribe(MQTT_TOPIC_SF_DISPLAY); + MyMqtt_Subscribe(MQTT_TOPIC_SF_CONFIG_SETUP); +} diff --git a/ADIS_ESP32_Eclipse/main/challenge_com.h b/ADIS_ESP32_Eclipse/main/challenge_com.h index 9617f9e..8d15acb 100644 --- a/ADIS_ESP32_Eclipse/main/challenge_com.h +++ b/ADIS_ESP32_Eclipse/main/challenge_com.h @@ -8,12 +8,12 @@ #ifndef MAIN_CHALLENGE_COM_H_ #define MAIN_CHALLENGE_COM_H_ -/* TOPICS */ -const char MQTT_TOPIC_SF_DISPLAY[] = "/splitFlap/cmd/display/"; -const char MQTT_TOPIC_SF_INITALL[] = "/splitFlap/cmd/init/"; -const char MQTT_TOPIC_SF_CONFIG_SETUP[] = "/splitFlap/config/setup/"; - +#include "mqtt_client.h" +/*! \brief parser for mqtt events */ +void Challenge_Com_ParseMqtt(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); +/*! \brief subscribes to all relevant topics */ +void Challenge_Com_SubscribeToAllTopics(void); #endif /* MAIN_CHALLENGE_COM_H_ */ diff --git a/ADIS_ESP32_Eclipse/main/platform.c b/ADIS_ESP32_Eclipse/main/platform.c index 9a5137f..29a6a78 100644 --- a/ADIS_ESP32_Eclipse/main/platform.c +++ b/ADIS_ESP32_Eclipse/main/platform.c @@ -108,6 +108,6 @@ void PL_Init(void) { ROBOT_Init(); #endif #if PL_CONFIG_CHALLENGE_APP_ACTIVATED - Application_Start(); + Challenge_App_Init(); #endif } diff --git a/ADIS_ESP32_Eclipse/main/splitflap_wrapper.c b/ADIS_ESP32_Eclipse/main/splitflap_wrapper.c index 43cb436..fb7828d 100644 --- a/ADIS_ESP32_Eclipse/main/splitflap_wrapper.c +++ b/ADIS_ESP32_Eclipse/main/splitflap_wrapper.c @@ -5,7 +5,10 @@ * Author: jonas */ +#include +#include #include "splitflap_wrapper.h" +#include "McuUtility.h" #include "rs485.h" #define RS_CMD_PREFIX "rs sendcmd SplitFlap " @@ -17,8 +20,9 @@ * If all splitflaps report to be initialized before the timeout, the return value is true */ bool SplitFlap_Wrapper_MoveAllToZeroPosition(void){ char cmd[BUF_SIZE] = RS_CMD_PREFIX; - McuUtility_strcat(cmd, sizeof cmd, "initAll"); - RS485_ParseCommand(cmd); + //McuUtility_strcat(cmd, sizeof cmd, "initAll"); + //RS485_ParseCommand(cmd); TODO + return true; } /* display a sentence on the splitflap combination @@ -26,9 +30,10 @@ bool SplitFlap_Wrapper_MoveAllToZeroPosition(void){ * returns true when all movements finished */ bool SplitFlap_Wrapper_Display(char sentence[]){ char cmd[BUF_SIZE] = RS_CMD_PREFIX; - McuUtility_strcat(cmd, sizeof cmd, "Display "); - McuUtility_strcat(cmd, sizeof cmd, sentence); - RS485_ParseCommand(cmd); + //McuUtility_strcat(cmd, sizeof cmd, "Display "); + //McuUtility_strcat(cmd, sizeof cmd, sentence); + //RS485_ParseCommand(cmd); TODO + return true; } /* sets the hardware identifier of a splitflap with in the combination @@ -36,11 +41,12 @@ bool SplitFlap_Wrapper_Display(char sentence[]){ bool SplitFlap_Wrapper_SetHardwareIdentifier(uint8_t id, uint8_t hwId){ char cmd[BUF_SIZE] = RS_CMD_PREFIX; char setupId_str[4] = {0}; char hardwareId_str[4] = {0}; - McuUtility_Num8uToStr(setupId_str, sizeof setupId_str, id); - McuUtility_Num8uToStr(hardwareId_str, sizeof hardwareId_str, hwId); - McuUtility_strcat(cmd, sizeof cmd, "setId "); - McuUtility_strcat(cmd, sizeof cmd, setupId_str); - McuUtility_strcat(cmd, sizeof cmd, " "); - McuUtility_strcat(cmd, sizeof cmd, hardwareId_str); - RS485_ParseCommand(cmd); + //McuUtility_Num8uToStr(setupId_str, sizeof setupId_str, id); + //McuUtility_Num8uToStr(hardwareId_str, sizeof hardwareId_str, hwId); + //McuUtility_strcat(cmd, sizeof cmd, "setId "); + //McuUtility_strcat(cmd, sizeof cmd, setupId_str); + //McuUtility_strcat(cmd, sizeof cmd, " "); + //McuUtility_strcat(cmd, sizeof cmd, hardwareId_str); + //RS485_ParseCommand(cmd); TODO + return true; } diff --git a/ADIS_ESP32_Eclipse/main/splitflap_wrapper.h b/ADIS_ESP32_Eclipse/main/splitflap_wrapper.h index f9ec113..83a9cdf 100644 --- a/ADIS_ESP32_Eclipse/main/splitflap_wrapper.h +++ b/ADIS_ESP32_Eclipse/main/splitflap_wrapper.h @@ -8,6 +8,9 @@ #ifndef MAIN_SPLITFLAP_WRAPPER_H_ #define MAIN_SPLITFLAP_WRAPPER_H_ +#include +#include + /* moves all split flaps to the zero position * After a timeout the initializtion is cancelled and the return value is false * If all splitflaps report to be initialized before the timeout, the return value is true */