├── pubsub_overview.png ├── docs ├── pubsub_overview.png ├── Embedded │ └── APIs │ │ └── C │ │ ├── Core.rst │ │ └── Utils.rst ├── index.rst ├── make.bat ├── Makefile └── conf.py ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── test ├── headers │ ├── test.h │ └── greatest.h └── c │ ├── emplace_uint_suite.c │ ├── update_int_suite.c │ ├── test.c │ ├── update_uint_suite.c │ ├── update_float_suite.c │ ├── emplace_string_suite.c │ ├── framing_outgoing_suite.c │ ├── emplace_float_suite.c │ ├── match_suite.c │ ├── update_string_suite.c │ ├── pub_sub_string_suite.c │ ├── emplace_int_suite.c │ ├── pub_sub_float_suite.c │ ├── attach_test.c │ ├── framing_incoming_suite.c │ ├── pub_sub_uint_suite.c │ ├── dictionnary_test.c │ └── pub_sub_int_suite.c ├── telemetry_version.h ├── src └── telemetry │ ├── headers │ ├── crc16.h │ ├── framing.h │ ├── dictionnary.h │ ├── telemetry_core.h │ └── telemetry_utils.h │ └── c │ ├── crc16.c │ ├── dictionnary.c │ ├── telemetry_utils.c │ ├── framing.c │ └── telemetry_core.c ├── .gitattributes ├── appveyor.yml ├── BUILD.md ├── LICENSE.md ├── .gitignore ├── CONTRIBUTING.md ├── gradlew.bat ├── README.md ├── tools └── testvectorsgenerator │ └── c │ └── main.c └── gradlew /pubsub_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Overdrivr/Telemetry/HEAD/pubsub_overview.png -------------------------------------------------------------------------------- /docs/pubsub_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Overdrivr/Telemetry/HEAD/docs/pubsub_overview.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Overdrivr/Telemetry/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /test/headers/test.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H_ 2 | #define TEST_H_ 3 | 4 | #include "greatest.h" 5 | #include "telemetry_core.h" 6 | #include "telemetry_utils.h" 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /telemetry_version.h: -------------------------------------------------------------------------------- 1 | #ifndef TELEMETRY_VERSION_H_ 2 | #define TELEMETRY_VERSION_H_ 3 | 4 | #define TELEMETRY_VERSION_MAJOR 2 5 | #define TELEMETRY_VERSION_MINOR 0 6 | #define TELEMETRY_VERSION_PATCH 0 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /docs/Embedded/APIs/C/Core.rst: -------------------------------------------------------------------------------- 1 | C API core functions 2 | ===================== 3 | 4 | With the core API, you can: 5 | * Publish to a topic 6 | * Subscribe a variable to a topic 7 | * Subscribe a function to a topic 8 | * Subscribe a function to all topics 9 | 10 | *In writing* 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 20 10:41:19 CET 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-bin.zip 7 | -------------------------------------------------------------------------------- /src/telemetry/headers/crc16.h: -------------------------------------------------------------------------------- 1 | #ifndef CRC16_H_ 2 | #define CRC16_H_ 3 | 4 | //From : https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks 5 | 6 | #include "stdint.h" 7 | 8 | uint16_t crc16(uint8_t * data, uint32_t lenght); 9 | uint16_t crc16_recursive(uint8_t byte, uint16_t remainder); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /docs/Embedded/APIs/C/Utils.rst: -------------------------------------------------------------------------------- 1 | C API helper functions 2 | ===================== 3 | 4 | With the utils API, you can: 5 | * Decode a raw received payload and place the value in a variable 6 | * Check a received payload matches a given topic 7 | * Check a received payload matches a given topic and a given payload type 8 | 9 | *In writing* 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.1.{build}-{branch} 2 | 3 | build: false 4 | 5 | environment: 6 | matrix: 7 | - platform: x86 8 | 9 | - platform: x64 10 | 11 | init: 12 | - "C:\\MinGW\\bin\\gcc.exe -v" 13 | 14 | install: 15 | - "SET PATH=%PATH%;C:\\MinGW\\bin" 16 | - "gcc -v" 17 | - "cd %APPVEYOR_BUILD_FOLDER%" 18 | - "gradlew installTestDebugExecutable" 19 | - "gradlew installTest_cppDebugExecutable" 20 | 21 | test_script: 22 | - "cd %APPVEYOR_BUILD_FOLDER%/build/install/test/debug/" 23 | - "test.bat" 24 | - "cd ../../test_cpp/debug" 25 | - "test_cpp.bat" 26 | -------------------------------------------------------------------------------- /src/telemetry/c/crc16.c: -------------------------------------------------------------------------------- 1 | #include "crc16.h" 2 | 3 | uint16_t crc16(uint8_t* data, uint32_t len) 4 | { 5 | uint16_t rem = 0; 6 | uint16_t i=0; 7 | for(i = 0 ; i < len ; i++) 8 | { 9 | rem = crc16_recursive(data[i],rem); 10 | } 11 | return rem; 12 | } 13 | 14 | uint16_t crc16_recursive(uint8_t byte, uint16_t remainder) 15 | { 16 | uint16_t n = 16; 17 | 18 | remainder = remainder ^ (byte << (n-8)); 19 | uint16_t j = 0; 20 | for(j = 1 ; j < 8 ; j++) 21 | { 22 | if(remainder & 0x8000) 23 | { 24 | remainder = (remainder << 1) ^ 0x1021; 25 | } 26 | else 27 | { 28 | remainder = remainder << 1; 29 | } 30 | remainder &= 0xffff; 31 | } 32 | 33 | return remainder; 34 | } 35 | -------------------------------------------------------------------------------- /src/telemetry/headers/framing.h: -------------------------------------------------------------------------------- 1 | #ifndef FRAMING_H_ 2 | #define FRAMING_H_ 3 | 4 | #include "stddef.h" 5 | #include "stdint.h" 6 | 7 | void initialize_framing(); 8 | // Outgoing data 9 | // Set storage for the outgoing frame 10 | void outgoing_storage(uint8_t * buf, uint32_t bufSize); 11 | 12 | void begin_frame(); 13 | void append(uint8_t byte); 14 | void append2(uint16_t twobytes); 15 | void append4(uint32_t fourbytes); 16 | uint32_t end_frame(); 17 | 18 | // Incoming data 19 | // Set storage for the incoming data 20 | void incoming_storage(uint8_t * buf, uint32_t bufSize); 21 | 22 | void set_on_incoming_frame(void (*callback)(uint8_t * storage, uint32_t occupiedSize)); 23 | void set_on_incoming_error(void (*callback)(int32_t errCode)); 24 | void feed(uint8_t byte); 25 | #endif 26 | -------------------------------------------------------------------------------- /src/telemetry/headers/dictionnary.h: -------------------------------------------------------------------------------- 1 | #ifndef TELEMETRY_DICTIONNARY_H_ 2 | #define TELEMETRY_DICTIONNARY_H_ 3 | 4 | #include "stdint.h" 5 | 6 | 7 | enum ptr_type { 8 | ptr_f32 = 0, 9 | ptr_u8 = 1, 10 | ptr_u16 = 2, 11 | ptr_u32 = 3, 12 | ptr_i8 = 4, 13 | ptr_i16 = 5, 14 | ptr_i32 = 6, 15 | ptr_function = 8 16 | }; 17 | 18 | typedef enum ptr_type ptr_type; 19 | 20 | struct nlist { /* table entry: */ 21 | struct nlist *next; /* next entry in chain */ 22 | char * key; 23 | 24 | // Table can store for a given key all following pointers 25 | float * ptr_f32; 26 | uint8_t * ptr_u8; 27 | uint16_t * ptr_u16; 28 | uint32_t * ptr_u32; 29 | int8_t * ptr_i8; 30 | int16_t * ptr_i16; 31 | int32_t * ptr_i32; 32 | void * ptr_function; 33 | }; 34 | 35 | #define HASHSIZE 101 36 | 37 | void init_table(struct nlist ** hashtab); 38 | 39 | /* lookup: look for s in hashtab */ 40 | struct nlist * lookup(struct nlist ** hashtab, const char * key); 41 | 42 | struct nlist * install(struct nlist ** hashtab, const char * key, void * ptr, ptr_type type); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | ## continuous integration 2 | [Project CI on Appveyor](https://ci.appveyor.com/project/Overdrivr/telemetry) 3 | 4 | ## Build 5 | ### C compiler 6 | Build and install telemetry and tests 7 | ```shell 8 | gradlew installTestDebugExecutable 9 | ``` 10 | 11 | Run tests 12 | ```shell 13 | cd ./build/install/test/debug/ 14 | test.bat 15 | ``` 16 | 17 | ### C++ compiler 18 | Build and install telemetry and tests 19 | ```shell 20 | gradlew installTest_cppDebugExecutable 21 | ``` 22 | 23 | Run tests 24 | ```shell 25 | cd ./build/install/test_cpp/debug/ 26 | test_cpp.bat 27 | ``` 28 | 29 | ### Generate test vector 30 | The test vector is a collection of communication frames generated by Telemetry, that 31 | can be used during testing to validate the behavior of a specific platform distribution (Arduino, Mbed, etc) 32 | they are working correctly. 33 | 34 | Build and install test vector generator 35 | 36 | ```shell 37 | gradlew installTestVectorsGeneratorDebug 38 | ``` 39 | 40 | Run it. Generated test file is located in `./build/install/testVectorsGenerator/debug/` 41 | ```shell 42 | cd ./build/install/testVectorsGenerator/debug/ 43 | testVectorsGenerator.bat 44 | ``` 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Rémi Bèges ( remi beges gmail com ) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/c/emplace_uint_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST emplace_uint8() 4 | { 5 | TM_msg dummy; 6 | uint8_t buf = 64; 7 | dummy.type = TM_uint8; 8 | dummy.buffer = (void *)&buf; 9 | dummy.size = 1; 10 | 11 | uint8_t destination; 12 | 13 | if(!emplace_u8(&dummy, &destination)) 14 | { 15 | FAIL(); 16 | } 17 | 18 | ASSERT_EQ_FMT(buf, destination,"%u"); 19 | 20 | PASS(); 21 | } 22 | 23 | TEST emplace_uint16() 24 | { 25 | TM_msg dummy; 26 | uint16_t buf = 64; 27 | dummy.type = TM_uint16; 28 | dummy.buffer = (void *)&buf; 29 | dummy.size = 1; 30 | 31 | uint16_t destination; 32 | 33 | if(!emplace_u16(&dummy, &destination)) 34 | { 35 | FAIL(); 36 | } 37 | 38 | ASSERT_EQ_FMT(buf, destination,"%d"); 39 | 40 | PASS(); 41 | } 42 | 43 | TEST emplace_uint32() 44 | { 45 | TM_msg dummy; 46 | uint32_t buf = 64; 47 | dummy.type = TM_uint32; 48 | dummy.buffer = (void *)&buf; 49 | dummy.size = 1; 50 | 51 | uint32_t destination; 52 | 53 | if(!emplace_u32(&dummy, &destination)) 54 | { 55 | FAIL(); 56 | } 57 | 58 | ASSERT_EQ_FMT(buf, destination,"%d"); 59 | 60 | PASS(); 61 | } 62 | 63 | SUITE(emplace_uint_suite) { 64 | RUN_TEST(emplace_uint8); 65 | RUN_TEST(emplace_uint16); 66 | RUN_TEST(emplace_uint32); 67 | } 68 | -------------------------------------------------------------------------------- /test/c/update_int_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST update_int8() 4 | { 5 | TM_msg dummy; 6 | char topic[128] = "int8"; 7 | int8_t buf = -64; 8 | dummy.type = TM_int8; 9 | dummy.buffer = (void *)&buf; 10 | dummy.size = 1; 11 | dummy.topic = topic; 12 | 13 | int8_t destination; 14 | 15 | if(!update_i8(&dummy, "int8", &destination)) 16 | { 17 | FAIL(); 18 | } 19 | ASSERT_EQ_FMT(buf, destination,"%u"); 20 | 21 | PASS(); 22 | } 23 | 24 | TEST update_int16() 25 | { 26 | TM_msg dummy; 27 | char topic[128] = "int16"; 28 | int16_t buf = -64; 29 | dummy.type = TM_int16; 30 | dummy.buffer = (void *)&buf; 31 | dummy.size = 1; 32 | dummy.topic = topic; 33 | 34 | int16_t destination; 35 | 36 | if(!update_i16(&dummy, "int16", &destination)) 37 | { 38 | FAIL(); 39 | } 40 | ASSERT_EQ_FMT(buf, destination,"%u"); 41 | 42 | PASS(); 43 | } 44 | 45 | 46 | TEST update_int32() 47 | { 48 | TM_msg dummy; 49 | char topic[128] = "int32"; 50 | int32_t buf = -64; 51 | dummy.type = TM_int32; 52 | dummy.buffer = (void *)&buf; 53 | dummy.size = 1; 54 | dummy.topic = topic; 55 | 56 | int32_t destination; 57 | 58 | if(!update_i32(&dummy, "int32", &destination)) 59 | { 60 | FAIL(); 61 | } 62 | ASSERT_EQ_FMT(buf, destination,"%u"); 63 | 64 | PASS(); 65 | } 66 | 67 | 68 | 69 | 70 | SUITE(update_int_suite) { 71 | RUN_TEST(update_int8); 72 | RUN_TEST(update_int16); 73 | RUN_TEST(update_int32); 74 | } 75 | -------------------------------------------------------------------------------- /test/c/test.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | extern SUITE(emplace_message_suite); 4 | extern SUITE(emplace_uint_suite); 5 | extern SUITE(emplace_int_suite); 6 | extern SUITE(emplace_float_suite); 7 | extern SUITE(pub_sub_string_suite); 8 | extern SUITE(framing_outgoing_suite); 9 | extern SUITE(framing_incoming_suite); 10 | extern SUITE(pub_sub_uint_suite); 11 | extern SUITE(pub_sub_int_suite); 12 | extern SUITE(pub_sub_float_suite); 13 | extern SUITE(update_message_suite); 14 | extern SUITE(update_uint_suite); 15 | extern SUITE(update_int_suite); 16 | extern SUITE(update_float_suite); 17 | extern SUITE(match_suite); 18 | extern SUITE(dictionnary_suite); 19 | extern SUITE(attach_suite); 20 | 21 | GREATEST_MAIN_DEFS(); 22 | 23 | int main(int argc, char **argv) { 24 | GREATEST_MAIN_BEGIN(); 25 | 26 | RUN_SUITE(emplace_message_suite); 27 | RUN_SUITE(emplace_uint_suite); 28 | RUN_SUITE(emplace_int_suite); 29 | RUN_SUITE(emplace_float_suite); 30 | RUN_SUITE(update_message_suite); 31 | RUN_SUITE(update_uint_suite); 32 | RUN_SUITE(update_int_suite); 33 | RUN_SUITE(update_float_suite); 34 | RUN_SUITE(framing_outgoing_suite); 35 | RUN_SUITE(framing_incoming_suite); 36 | RUN_SUITE(pub_sub_string_suite); 37 | RUN_SUITE(pub_sub_uint_suite); 38 | RUN_SUITE(pub_sub_int_suite); 39 | RUN_SUITE(pub_sub_float_suite); 40 | RUN_SUITE(match_suite); 41 | RUN_SUITE(dictionnary_suite); 42 | RUN_SUITE(attach_suite); 43 | 44 | GREATEST_MAIN_END(); 45 | } 46 | -------------------------------------------------------------------------------- /test/c/update_uint_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST update_uint8() 4 | { 5 | TM_msg dummy; 6 | char topic[128] = "uint8"; 7 | uint8_t buf = 64; 8 | dummy.type = TM_uint8; 9 | dummy.buffer = (void *)&buf; 10 | dummy.size = 1; 11 | dummy.topic = topic; 12 | 13 | uint8_t destination; 14 | 15 | if(!update_u8(&dummy, "uint8", &destination)) 16 | { 17 | FAIL(); 18 | } 19 | ASSERT_EQ_FMT(buf, destination,"%u"); 20 | 21 | PASS(); 22 | } 23 | 24 | TEST update_uint16() 25 | { 26 | TM_msg dummy; 27 | char topic[128] = "uint16"; 28 | uint16_t buf = 64; 29 | dummy.type = TM_uint16; 30 | dummy.buffer = (void *)&buf; 31 | dummy.size = 1; 32 | dummy.topic = topic; 33 | 34 | uint16_t destination; 35 | 36 | if(!update_u16(&dummy, "uint16", &destination)) 37 | { 38 | FAIL(); 39 | } 40 | ASSERT_EQ_FMT(buf, destination,"%u"); 41 | 42 | PASS(); 43 | } 44 | 45 | 46 | TEST update_uint32() 47 | { 48 | TM_msg dummy; 49 | char topic[128] = "uint32"; 50 | uint32_t buf = 64; 51 | dummy.type = TM_uint32; 52 | dummy.buffer = (void *)&buf; 53 | dummy.size = 1; 54 | dummy.topic = topic; 55 | 56 | uint32_t destination; 57 | 58 | if(!update_u32(&dummy, "uint32", &destination)) 59 | { 60 | FAIL(); 61 | } 62 | ASSERT_EQ_FMT(buf, destination,"%u"); 63 | 64 | PASS(); 65 | } 66 | 67 | 68 | 69 | 70 | SUITE(update_uint_suite) { 71 | RUN_TEST(update_uint8); 72 | RUN_TEST(update_uint16); 73 | RUN_TEST(update_uint32); 74 | } 75 | -------------------------------------------------------------------------------- /test/c/update_float_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST update_float32() 4 | { 5 | TM_msg dummy; 6 | char topic[128] = "float32"; 7 | float buf = -64; 8 | dummy.type = TM_float32; 9 | dummy.buffer = (void *)&buf; 10 | dummy.size = 1; 11 | dummy.topic = topic; 12 | 13 | float destination; 14 | 15 | if(!update_f32(&dummy, "float32", &destination)) 16 | { 17 | FAIL(); 18 | } 19 | ASSERT_EQ_FMT(buf, destination,"%u"); 20 | 21 | PASS(); 22 | } 23 | 24 | TEST update_float32_neg() 25 | { 26 | TM_msg dummy; 27 | char topic[128] = "float32_neg"; 28 | float buf = -64; 29 | dummy.type = TM_float32; 30 | dummy.buffer = (void *)&buf; 31 | dummy.size = 1; 32 | dummy.topic = topic; 33 | 34 | float destination; 35 | 36 | if(!update_f32(&dummy, "float32_neg", &destination)) 37 | { 38 | FAIL(); 39 | } 40 | ASSERT_EQ_FMT(buf, destination,"%u"); 41 | 42 | PASS(); 43 | } 44 | 45 | 46 | TEST update_float32_decimals() 47 | { 48 | TM_msg dummy; 49 | char topic[128] = "float32_decimals"; 50 | float buf = -64.1235; 51 | dummy.type = TM_float32; 52 | dummy.buffer = (void *)&buf; 53 | dummy.size = 1; 54 | dummy.topic = topic; 55 | 56 | float destination; 57 | 58 | if(!update_f32(&dummy, "float32_decimals", &destination)) 59 | { 60 | FAIL(); 61 | } 62 | ASSERT_EQ_FMT(buf, destination,"%u"); 63 | 64 | PASS(); 65 | } 66 | 67 | SUITE(update_float_suite) { 68 | RUN_TEST(update_float32); 69 | RUN_TEST(update_float32_neg); 70 | RUN_TEST(update_float32_decimals); 71 | } 72 | -------------------------------------------------------------------------------- /src/telemetry/headers/telemetry_core.h: -------------------------------------------------------------------------------- 1 | #ifndef TELEMETRY_CORE_H_ 2 | #define TELEMETRY_CORE_H_ 3 | 4 | #include "stddef.h" 5 | #include "stdint.h" 6 | #include "telemetry_utils.h" 7 | 8 | #define INCOMING_BUFFER_SIZE 128 9 | #define OUTGOING_BUFFER_SIZE 128 10 | #define TOPIC_BUFFER_SIZE 64 11 | 12 | void attach(const char * name, void (*callback)(TM_msg * m)); 13 | void attach_f32(const char * name, float * variable); 14 | void attach_u8(const char * name, uint8_t * variable); 15 | void attach_u16(const char * name, uint16_t * variable); 16 | void attach_u32(const char * name, uint32_t * variable); 17 | void attach_i8(const char * name, int8_t * variable); 18 | void attach_i16(const char * name, int16_t * variable); 19 | void attach_i32(const char * name, int32_t * variable); 20 | 21 | void init_telemetry(TM_transport * t); 22 | 23 | void publish(const char * topic, const char * msg); 24 | void publish_u8(const char * topic, uint8_t msg); 25 | void publish_u16(const char * topic, uint16_t msg); 26 | void publish_u32(const char * topic, uint32_t msg); 27 | void publish_i8(const char * topic, int8_t msg); 28 | void publish_i16(const char * topic, int16_t msg); 29 | void publish_i32(const char * topic, int32_t msg); 30 | void publish_f32(const char * topic, float msg); 31 | 32 | // subscribe a function to be called everytime a frame is received 33 | // second argument is a data structure that you can implement to access your program data inside the function 34 | void subscribe(void (*callback)(TM_state * s, TM_msg * m), TM_state * s); 35 | 36 | void update_telemetry(); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /test/c/emplace_string_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST emplace_string() 4 | { 5 | TM_msg dummy; 6 | char buf[] = "Hello, World!"; 7 | dummy.type = TM_string; 8 | dummy.buffer = (void *)buf; 9 | dummy.size = strlen(buf); 10 | 11 | char destination[32]; 12 | 13 | if(!emplace(&dummy, destination, 32)) 14 | { 15 | FAIL(); 16 | } 17 | 18 | ASSERT_EQ_FMT(strlen(destination), strlen(buf),"%d"); 19 | ASSERT_STR_EQ(destination, buf); 20 | 21 | PASS(); 22 | } 23 | 24 | TEST emplace_string_exact_fit() 25 | { 26 | TM_msg dummy; 27 | char buf[] = "Hello, World!"; 28 | dummy.type = TM_string; 29 | dummy.buffer = (void *)buf; 30 | dummy.size = strlen(buf); 31 | 32 | char destination[14]; 33 | 34 | if(!emplace(&dummy, destination, 14)) 35 | { 36 | FAIL(); 37 | } 38 | 39 | ASSERT_EQ_FMT(strlen(destination), strlen(buf),"%d"); 40 | ASSERT_STR_EQ(destination, buf); 41 | 42 | PASS(); 43 | } 44 | 45 | TEST emplace_string_truncated() 46 | { 47 | TM_msg dummy; 48 | char buf[] = "Hello, World!"; 49 | dummy.type = TM_string; 50 | dummy.buffer = (void *)buf; 51 | dummy.size = strlen(buf); 52 | 53 | char destination[10]; 54 | 55 | if(!emplace(&dummy, destination, 10)) 56 | { 57 | FAIL(); 58 | } 59 | 60 | char expected[] = "Hello, Wo"; 61 | 62 | ASSERT_EQ_FMT(strlen(destination), strlen(expected),"%d"); 63 | ASSERT_STR_EQ(destination, expected); 64 | 65 | PASS(); 66 | } 67 | 68 | SUITE(emplace_message_suite) { 69 | RUN_TEST(emplace_string); 70 | RUN_TEST(emplace_string_exact_fit); 71 | RUN_TEST(emplace_string_truncated); 72 | } 73 | -------------------------------------------------------------------------------- /test/c/framing_outgoing_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "framing.h" 3 | 4 | TEST framing_simple_frame() 5 | { 6 | uint8_t outgoingBuffer[12] = {0}; 7 | 8 | initialize_framing(); 9 | outgoing_storage(outgoingBuffer,12); 10 | 11 | begin_frame(); 12 | append(0xFF); 13 | uint32_t amount = end_frame(); 14 | 15 | ASSERT_EQ_FMT(3,amount,"%d"); 16 | 17 | uint8_t expected[] = {0xF7, 0xFF, 0x7F}; 18 | uint16_t range = amount > 3 ? 3 : amount; 19 | uint16_t i; 20 | 21 | for(i = 0 ; i < range ; i++) 22 | { 23 | ASSERT_EQ_FMT(expected[i],outgoingBuffer[i],"%x"); 24 | } 25 | 26 | PASS(); 27 | } 28 | 29 | TEST framing_with_escaping() 30 | { 31 | uint8_t outgoingBuffer[12] = {0}; 32 | 33 | initialize_framing(); 34 | outgoing_storage(outgoingBuffer,12); 35 | 36 | begin_frame(); 37 | append(0xF7); 38 | append(0x7F); 39 | append(0x7D); 40 | uint32_t amount = end_frame(); 41 | 42 | ASSERT_EQ_FMT(8,amount,"%d"); 43 | 44 | uint8_t expected[] = {0xF7, 0x7D, 0xF7, 0x7D, 0x7F, 0x7D, 0x7D, 0x7F}; 45 | uint16_t range = amount > 8 ? 8 : amount; 46 | uint32_t i; 47 | for(i = 0 ; i < range ; i++) 48 | { 49 | ASSERT_EQ_FMT(expected[i],outgoingBuffer[i],"%x"); 50 | } 51 | 52 | PASS(); 53 | } 54 | 55 | TEST framing_overflow() 56 | { 57 | uint8_t outgoingBuffer[3] = {0}; 58 | 59 | initialize_framing(); 60 | outgoing_storage(outgoingBuffer,3); 61 | 62 | begin_frame(); 63 | append(0xFF); 64 | append(0xFF); 65 | uint32_t amount = end_frame(); 66 | 67 | ASSERT_EQ_FMT(0,amount,"%d"); 68 | 69 | PASS(); 70 | } 71 | 72 | SUITE(framing_outgoing_suite) { 73 | RUN_TEST(framing_simple_frame); 74 | RUN_TEST(framing_with_escaping); 75 | RUN_TEST(framing_overflow); 76 | } 77 | -------------------------------------------------------------------------------- /test/c/emplace_float_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST emplace_float32() 4 | { 5 | TM_msg dummy; 6 | float buf = 64; 7 | dummy.type = TM_float32; 8 | dummy.buffer = (void *)&buf; 9 | dummy.size = 1; 10 | 11 | float destination; 12 | 13 | if(!emplace_f32(&dummy, &destination)) 14 | { 15 | FAIL(); 16 | } 17 | 18 | ASSERT_EQ_FMT(buf, destination,"%f"); 19 | 20 | PASS(); 21 | } 22 | 23 | TEST emplace_float32_neg() 24 | { 25 | TM_msg dummy; 26 | float buf = -64; 27 | dummy.type = TM_float32; 28 | dummy.buffer = (void *)&buf; 29 | dummy.size = 1; 30 | 31 | float destination; 32 | 33 | if(!emplace_f32(&dummy, &destination)) 34 | { 35 | FAIL(); 36 | } 37 | 38 | ASSERT_EQ_FMT(buf, destination,"%f"); 39 | 40 | PASS(); 41 | } 42 | 43 | TEST emplace_float32_decimals() 44 | { 45 | TM_msg dummy; 46 | float buf = 64.123456; 47 | dummy.type = TM_float32; 48 | dummy.buffer = (void *)&buf; 49 | dummy.size = 1; 50 | 51 | float destination; 52 | 53 | if(!emplace_f32(&dummy, &destination)) 54 | { 55 | FAIL(); 56 | } 57 | 58 | ASSERT_EQ_FMT(buf, destination,"%f"); 59 | 60 | PASS(); 61 | } 62 | 63 | TEST emplace_float32_decimals_neg() 64 | { 65 | TM_msg dummy; 66 | float buf = -64.123456; 67 | dummy.type = TM_float32; 68 | dummy.buffer = (void *)&buf; 69 | dummy.size = 1; 70 | 71 | float destination; 72 | 73 | if(!emplace_f32(&dummy, &destination)) 74 | { 75 | FAIL(); 76 | } 77 | 78 | ASSERT_EQ_FMT(buf, destination,"%f"); 79 | 80 | PASS(); 81 | } 82 | 83 | SUITE(emplace_float_suite) { 84 | RUN_TEST(emplace_float32); 85 | RUN_TEST(emplace_float32_neg); 86 | RUN_TEST(emplace_float32_decimals); 87 | RUN_TEST(emplace_float32_decimals_neg); 88 | } 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # ========================= 35 | # Operating System Files 36 | # ========================= 37 | 38 | # OSX 39 | # ========================= 40 | 41 | .DS_Store 42 | .AppleDouble 43 | .LSOverride 44 | 45 | # Thumbnails 46 | ._* 47 | 48 | # Files that might appear in the root of a volume 49 | .DocumentRevisions-V100 50 | .fseventsd 51 | .Spotlight-V100 52 | .TemporaryItems 53 | .Trashes 54 | .VolumeIcon.icns 55 | 56 | # Directories potentially created on remote AFP share 57 | .AppleDB 58 | .AppleDesktop 59 | Network Trash Folder 60 | Temporary Items 61 | .apdisk 62 | 63 | # Windows 64 | # ========================= 65 | 66 | # Windows image file caches 67 | Thumbs.db 68 | ehthumbs.db 69 | 70 | # Folder config file 71 | Desktop.ini 72 | 73 | # Recycle Bin used on file shares 74 | $RECYCLE.BIN/ 75 | 76 | # Windows Installer files 77 | *.cab 78 | *.msi 79 | *.msm 80 | *.msp 81 | 82 | # Windows shortcuts 83 | *.lnk 84 | 85 | # ========================= 86 | # Gradle Files 87 | # ========================= 88 | 89 | .gradle 90 | build/ 91 | 92 | # Ignore Gradle GUI config 93 | gradle-app.setting 94 | 95 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 96 | !gradle-wrapper.jar 97 | 98 | # =============== 99 | # Distribution folder 100 | dist/ 101 | -------------------------------------------------------------------------------- /test/c/match_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST match_simple() 4 | { 5 | TM_msg dummy; 6 | char topic[] = "foo"; 7 | dummy.topic = topic; 8 | 9 | ASSERT_EQ_FMT(match(&dummy, topic),1,"%u"); 10 | ASSERT_EQ_FMT(match(&dummy, "foo"),1,"%u"); 11 | ASSERT_EQ_FMT(match(&dummy, "bar"),0,"%u"); 12 | PASS(); 13 | } 14 | 15 | TEST match_with_spaces() 16 | { 17 | TM_msg dummy; 18 | char topic[] = "foo with spaces"; 19 | dummy.topic = topic; 20 | 21 | ASSERT_EQ_FMT(match(&dummy, topic),1,"%u"); 22 | ASSERT_EQ_FMT(match(&dummy, "foo with spaces"),1,"%u"); 23 | ASSERT_EQ_FMT(match(&dummy, "foo"),0,"%u"); 24 | ASSERT_EQ_FMT(match(&dummy, "bar"),0,"%u"); 25 | PASS(); 26 | } 27 | 28 | TEST match_with_special_chars() 29 | { 30 | TM_msg dummy; 31 | char topic[] = "/:@#"; 32 | dummy.topic = topic; 33 | 34 | ASSERT_EQ_FMT(match(&dummy, topic),1,"%u"); 35 | ASSERT_EQ_FMT(match(&dummy, "/:@#"),1,"%u"); 36 | ASSERT_EQ_FMT(match(&dummy, "$#@:/"),0,"%u"); 37 | ASSERT_EQ_FMT(match(&dummy, "bar"),0,"%u"); 38 | PASS(); 39 | } 40 | 41 | TEST fullmatch_test() 42 | { 43 | TM_msg dummy; 44 | char topic[] = "foo"; 45 | dummy.topic = topic; 46 | dummy.type = TM_uint8; 47 | 48 | ASSERT_EQ_FMT(fullmatch(&dummy, topic, TM_uint8),1,"%u"); 49 | ASSERT_EQ_FMT(fullmatch(&dummy, topic, TM_int8),0,"%u"); 50 | ASSERT_EQ_FMT(fullmatch(&dummy, "foo", TM_uint8),1,"%u"); 51 | ASSERT_EQ_FMT(fullmatch(&dummy, "foo", TM_int8),0,"%u"); 52 | ASSERT_EQ_FMT(fullmatch(&dummy, "bar", TM_uint8),0,"%u"); 53 | ASSERT_EQ_FMT(fullmatch(&dummy, "bar", TM_int16),0,"%u"); 54 | PASS(); 55 | } 56 | 57 | SUITE(match_suite) { 58 | RUN_TEST(match_simple); 59 | RUN_TEST(match_with_spaces); 60 | RUN_TEST(match_with_special_chars); 61 | RUN_TEST(fullmatch_test); 62 | } 63 | -------------------------------------------------------------------------------- /test/c/update_string_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST update_string() 4 | { 5 | TM_msg dummy; 6 | char buf[] = "Hello, World!"; 7 | char topic[128] = "string"; 8 | dummy.type = TM_string; 9 | dummy.buffer = (void *)buf; 10 | dummy.size = strlen(buf); 11 | dummy.topic = topic; 12 | 13 | char destination[32]; 14 | 15 | if(!update(&dummy, topic, destination, 32)) 16 | { 17 | FAIL(); 18 | } 19 | 20 | ASSERT_EQ_FMT(strlen(destination), strlen(buf),"%d"); 21 | ASSERT_STR_EQ(destination, buf); 22 | 23 | PASS(); 24 | } 25 | 26 | TEST update_string_exact_fit() 27 | { 28 | TM_msg dummy; 29 | char buf[] = "Hello, World!"; 30 | char topic[128] = "string_exact_fit"; 31 | dummy.type = TM_string; 32 | dummy.buffer = (void *)buf; 33 | dummy.size = strlen(buf); 34 | dummy.topic = topic; 35 | 36 | char destination[14]; 37 | 38 | if(!update(&dummy, topic, destination, 14)) 39 | { 40 | FAIL(); 41 | } 42 | 43 | ASSERT_EQ_FMT(strlen(destination), strlen(buf),"%d"); 44 | ASSERT_STR_EQ(destination, buf); 45 | 46 | PASS(); 47 | } 48 | 49 | TEST update_string_truncated() 50 | { 51 | TM_msg dummy; 52 | char buf[] = "Hello, World!"; 53 | char topic[128] = "string_truncated"; 54 | dummy.type = TM_string; 55 | dummy.buffer = (void *)buf; 56 | dummy.size = strlen(buf); 57 | dummy.topic = topic; 58 | 59 | char destination[10]; 60 | 61 | if(!update(&dummy,topic, destination, 10)) 62 | { 63 | FAIL(); 64 | } 65 | 66 | char expected[] = "Hello, Wo"; 67 | 68 | ASSERT_EQ_FMT(strlen(destination), strlen(expected),"%d"); 69 | ASSERT_STR_EQ(destination, expected); 70 | 71 | PASS(); 72 | } 73 | 74 | SUITE(update_message_suite) { 75 | RUN_TEST(update_string); 76 | RUN_TEST(update_string_exact_fit); 77 | RUN_TEST(update_string_truncated); 78 | } 79 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love pull requests from everyone. If you wish to contribute, here is how to do it. 4 | 5 | You can contribute to Telemetry in two different aspects: 6 | * Core library: the logic that powers the library 7 | * Platform interfaces: Convenient *batteries-included* wrappers of Telemetry for platforms like Arduino, ARM mbed, etc. 8 | 9 | If you wish to contribute to the core library, make sure to open a ticket first to propose your ide. 10 | The goal is to validate proposed changes before you dive into the code. 11 | 12 | Then, fork the repository and clone it onto your machine. 13 | 14 | ``` 15 | git clone git@github.com:your-username/Telemetry.git 16 | ``` 17 | 18 | Make the changes, then open a Pull Request. 19 | 20 | If you wish to contribute to an existing Telemetry wrapper for Arduino or Mbed, 21 | see their respective repositories for contribution: 22 | * [Telemetry-arduino](https://github.com/Overdrivr/Telemetry-arduino) 23 | * [Telemetry-mbed](https://github.com/Overdrivr/Telemetry-mbed) 24 | 25 | If you wish to implement a new platform, feel free to create a new repository 26 | yourself if you intend to maintain it yourself, or open a ticket to gracefully ask for it. 27 | 28 | # Core library contributions 29 | 30 | Inside the project folder, compile the core library and run the tests : 31 | 32 | ```shell 33 | gradlew installTestDebugExecutable 34 | cd ./build/install/test/debug/ 35 | test.bat 36 | ``` 37 | 38 | Make the appropriated changes, add new tests to ensure your additions are running smoothly. 39 | 40 | Build and test again. If everything goes well, propose a pull request. 41 | 42 | Your changes will be tested automatically on both continuous integration servers. 43 | You can check the tests status on your pull request page. 44 | 45 | At this point, your pull request will be reviewed. 46 | Either it will be accepted, or you will receive some feedback before it can be accepted. 47 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Telemetry documentation master file, created by 2 | sphinx-quickstart on Thu Apr 14 11:36:29 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Telemetry's documentation! 7 | ===================================== 8 | 9 | The ``Telemetry`` project is a collection of tools for *remote data-visualization 10 | and communication* with embedded devices. 11 | 12 | .. image:: https://raw.githubusercontent.com/Overdrivr/Telemetry/master/docs/pubsub_overview.png 13 | 14 | 15 | At its core, `Telemetry` is a communication protocol. 16 | 17 | Data is exchanged on named channels, called *topics* (ex : `foo`, `bar` and `qux` on the figure above). 18 | 19 | Sending data is called *publishing*. 20 | 21 | .. code-block:: cpp 22 | 23 | Telemetry TM; 24 | int32_t i = 123; 25 | 26 | TM.pub_i32("foo", i); 27 | 28 | 29 | Receiving data is called *subscribing*. 30 | A variable attached to a topic will be updated each time new data is received under this topic. 31 | 32 | .. code-block:: cpp 33 | 34 | Telemetry TM; 35 | float throttle; 36 | 37 | TM.attach_f32_to("throttle", &throttle); 38 | 39 | for(;;) { 40 | TM.update(); 41 | } 42 | 43 | 44 | Extra tools are build around this protocol to provide a fun and easy way to communicate with the device from a computer in real-time. 45 | 46 | 47 | The Telemetry project is constituted of different libraries at the moment: 48 | 49 | * **Protocol implementations** 50 | 51 | * ``Telemetry``: Portable C/C++ implementation of the protocol. Runs on embedded platforms (``Arduino``, ``Mbed``, etc.). 52 | * ``Pytelemetry``: Python implementation of the protocol. Runs on Windows, Mac OS, Linux 53 | 54 | * **Desktop Tools** 55 | 56 | * ``Pytelemetrycli``: Smart command line interface. Open **live plots** on received data from a single command, 57 | reconfigure device in real-time, logging, network analysis, etc. 58 | Runs on Windows, Mac OS and Linux 59 | 60 | Contents: 61 | 62 | 63 | .. toctree:: 64 | :maxdepth: 4 65 | 66 | 67 | 68 | Indices and tables 69 | ================== 70 | 71 | * :ref:`genindex` 72 | * :ref:`modindex` 73 | * :ref:`search` 74 | -------------------------------------------------------------------------------- /test/c/pub_sub_string_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | /// Mock of transport (serial). write is piped back to read 4 | static uint8_t endBuffer[OUTGOING_BUFFER_SIZE]; 5 | static uint32_t sizeWritten; 6 | static uint32_t sizeRead; 7 | 8 | int32_t read_str(uint8_t * buf, uint32_t sizeToRead) 9 | { 10 | int32_t rem = sizeWritten - sizeRead; 11 | uint16_t range = sizeToRead > rem ? rem : sizeToRead; 12 | int32_t i; 13 | for(i = 0 ; i < range ; i++) 14 | { 15 | buf[i] = endBuffer[sizeRead + i]; 16 | sizeRead++; 17 | } 18 | 19 | } 20 | 21 | int32_t readable_str() 22 | { 23 | return sizeWritten; 24 | } 25 | 26 | int32_t write_str(uint8_t * buf, uint32_t sizeToWrite) 27 | { 28 | sizeWritten = sizeToWrite; 29 | int32_t i; 30 | for(i = 0 ; i < sizeToWrite ; i++) 31 | { 32 | endBuffer[i] = buf[i]; 33 | } 34 | } 35 | 36 | int32_t writeable_str() 37 | { 38 | return 1; 39 | } 40 | 41 | /// end of mock 42 | 43 | struct TM_state { 44 | uint8_t called; 45 | char rcvString[OUTGOING_BUFFER_SIZE]; 46 | char rcvTopic[OUTGOING_BUFFER_SIZE]; 47 | }; 48 | 49 | void callback_str(TM_state* s, TM_msg* m) 50 | { 51 | s->called = 1; 52 | char str[OUTGOING_BUFFER_SIZE] = {0}; 53 | if(emplace(m,str,OUTGOING_BUFFER_SIZE)) 54 | { 55 | strcpy(s->rcvString,str); 56 | strcpy(s->rcvTopic,m->topic); 57 | } 58 | } 59 | 60 | TEST publish_string() 61 | { 62 | TM_state state; 63 | uint16_t i; 64 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 65 | { 66 | endBuffer[i] = 0; 67 | state.rcvTopic[i] = 0; 68 | state.rcvString[i] = 0; 69 | } 70 | sizeWritten = 0; 71 | sizeRead = 0; 72 | state.called = 0; 73 | 74 | TM_transport transport; 75 | transport.read = read_str; 76 | transport.write = write_str; 77 | transport.readable = readable_str; 78 | transport.writeable = writeable_str; 79 | 80 | char topic[] = "topic"; 81 | char message[] = "someMessage"; 82 | 83 | init_telemetry(&transport); 84 | 85 | subscribe(callback_str,&state); 86 | 87 | publish(topic, message); 88 | 89 | update_telemetry(); 90 | 91 | ASSERT_EQ(state.called, 1); 92 | ASSERT_STR_EQ(message,state.rcvString); 93 | ASSERT_STR_EQ(topic,state.rcvTopic); 94 | 95 | PASS(); 96 | } 97 | 98 | SUITE(pub_sub_string_suite) { 99 | RUN_TEST(publish_string); 100 | } 101 | -------------------------------------------------------------------------------- /test/c/emplace_int_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST emplace_int8() 4 | { 5 | TM_msg dummy; 6 | int8_t buf = 64; 7 | dummy.type = TM_int8; 8 | dummy.buffer = (void *)&buf; 9 | dummy.size = 1; 10 | 11 | int8_t destination; 12 | 13 | if(!emplace_i8(&dummy, &destination)) 14 | { 15 | FAIL(); 16 | } 17 | 18 | ASSERT_EQ_FMT(buf, destination,"%u"); 19 | 20 | PASS(); 21 | } 22 | 23 | TEST emplace_int8_neg() 24 | { 25 | TM_msg dummy; 26 | int8_t buf = -64; 27 | dummy.type = TM_int8; 28 | dummy.buffer = (void *)&buf; 29 | dummy.size = 1; 30 | 31 | int8_t destination; 32 | 33 | if(!emplace_i8(&dummy, &destination)) 34 | { 35 | FAIL(); 36 | } 37 | 38 | ASSERT_EQ_FMT(buf, destination,"%u"); 39 | 40 | PASS(); 41 | } 42 | 43 | TEST emplace_int16() 44 | { 45 | TM_msg dummy; 46 | int16_t buf = 64; 47 | dummy.type = TM_int16; 48 | dummy.buffer = (void *)&buf; 49 | dummy.size = 1; 50 | 51 | int16_t destination; 52 | 53 | if(!emplace_i16(&dummy, &destination)) 54 | { 55 | FAIL(); 56 | } 57 | 58 | ASSERT_EQ_FMT(buf, destination,"%d"); 59 | 60 | PASS(); 61 | } 62 | 63 | TEST emplace_int16_neg() 64 | { 65 | TM_msg dummy; 66 | int16_t buf = -64; 67 | dummy.type = TM_int16; 68 | dummy.buffer = (void *)&buf; 69 | dummy.size = 1; 70 | 71 | int16_t destination; 72 | 73 | if(!emplace_i16(&dummy, &destination)) 74 | { 75 | FAIL(); 76 | } 77 | 78 | ASSERT_EQ_FMT(buf, destination,"%d"); 79 | 80 | PASS(); 81 | } 82 | 83 | TEST emplace_int32() 84 | { 85 | TM_msg dummy; 86 | int32_t buf = 64; 87 | dummy.type = TM_int32; 88 | dummy.buffer = (void *)&buf; 89 | dummy.size = 1; 90 | 91 | int32_t destination; 92 | 93 | if(!emplace_i32(&dummy, &destination)) 94 | { 95 | FAIL(); 96 | } 97 | 98 | ASSERT_EQ_FMT(buf, destination,"%d"); 99 | 100 | PASS(); 101 | } 102 | 103 | TEST emplace_int32_neg() 104 | { 105 | TM_msg dummy; 106 | int32_t buf = -64; 107 | dummy.type = TM_int32; 108 | dummy.buffer = (void *)&buf; 109 | dummy.size = 1; 110 | 111 | int32_t destination; 112 | 113 | if(!emplace_i32(&dummy, &destination)) 114 | { 115 | FAIL(); 116 | } 117 | 118 | ASSERT_EQ_FMT(buf, destination,"%d"); 119 | 120 | PASS(); 121 | } 122 | 123 | SUITE(emplace_int_suite) { 124 | RUN_TEST(emplace_int8); 125 | RUN_TEST(emplace_int16); 126 | RUN_TEST(emplace_int32); 127 | RUN_TEST(emplace_int8_neg); 128 | RUN_TEST(emplace_int16_neg); 129 | RUN_TEST(emplace_int32_neg); 130 | } 131 | -------------------------------------------------------------------------------- /src/telemetry/headers/telemetry_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef TELEMETRY_UTILS_H_ 2 | #define TELEMETRY_UTILS_H_ 3 | 4 | #include "stddef.h" 5 | #include "stdint.h" 6 | #include "string.h" 7 | 8 | // Forward declaration of user state 9 | typedef struct TM_state TM_state; 10 | 11 | // Enumeration of supported message payloads 12 | enum TM_type { 13 | TM_float32 = 0, 14 | TM_uint8 = 1, 15 | TM_uint16 = 2, 16 | TM_uint32 = 3, 17 | TM_int8 = 4, 18 | TM_int16 = 5, 19 | TM_int32 = 6, 20 | TM_string = 7 21 | }; 22 | 23 | typedef enum TM_type TM_type; 24 | 25 | 26 | // Data structure for received messages 27 | typedef struct TM_msg TM_msg; 28 | struct TM_msg { 29 | TM_type type; 30 | char * topic; 31 | void * buffer; 32 | uint32_t size; 33 | }; 34 | 35 | // Data structure for holding transport interface 36 | typedef struct TM_transport TM_transport; 37 | struct TM_transport { 38 | int32_t (*read)(uint8_t * buf, uint32_t sizeToRead); 39 | int32_t (*readable)(); 40 | int32_t (*write)(uint8_t * buf, uint32_t sizeToWrite); 41 | int32_t (*writeable)(); 42 | }; 43 | 44 | // Decodes TM_msg buffer and emplaces its value into dst 45 | // Returns 1 (true) if decoding was successful 46 | uint32_t emplace(TM_msg * m, char * buf, size_t bufSize); 47 | uint32_t emplace_u8(TM_msg * m, uint8_t * dst); 48 | uint32_t emplace_u16(TM_msg * m, uint16_t * dst); 49 | uint32_t emplace_u32(TM_msg * m, uint32_t * dst); 50 | uint32_t emplace_i8(TM_msg * m, int8_t * dst); 51 | uint32_t emplace_i16(TM_msg * m, int16_t * dst); 52 | uint32_t emplace_i32(TM_msg * m, int32_t * dst); 53 | uint32_t emplace_f32(TM_msg * m, float * dst); 54 | 55 | // Returns 1 if topicToMatch matches m->topic 56 | // 0 otherwise 57 | uint32_t match(TM_msg * m, const char * topicToMatch); 58 | 59 | // Returns 1 if topicToMatch matches m->topic and typeToMatch matches m->type, 60 | // 0 otherwise 61 | uint32_t fullmatch(TM_msg * m, const char * topicToMatch, TM_type typeToMatch); 62 | 63 | // Decodes TM_msg buffer and update its value into dst if matching topic 64 | // Returns 1 (true) if decoding was successful 65 | uint32_t update(TM_msg * msg, const char *topic, char *var, size_t bufSize); 66 | uint32_t update_u8(TM_msg * msg, const char *topic, uint8_t *var); 67 | uint32_t update_u16(TM_msg * msg, const char *topic, uint16_t *var); 68 | uint32_t update_u32(TM_msg * msg, const char *topic, uint32_t *var); 69 | uint32_t update_i8(TM_msg * msg, const char *topic, int8_t *var); 70 | uint32_t update_i16(TM_msg * msg, const char *topic, int16_t *var); 71 | uint32_t update_i32(TM_msg * msg, const char *topic, int32_t *var); 72 | uint32_t update_f32(TM_msg * msg, const char *topic, float *var); 73 | 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/telemetry/c/dictionnary.c: -------------------------------------------------------------------------------- 1 | #include "dictionnary.h" 2 | #include "stdlib.h" 3 | #include "stdint.h" 4 | #include "string.h" 5 | 6 | /* hash: form hash value for string s */ 7 | unsigned hash(const char * s) 8 | { 9 | unsigned hashval; 10 | for (hashval = 0; *s != '\0'; s++) 11 | hashval = *s + 31 * hashval; 12 | return hashval % HASHSIZE; 13 | } 14 | 15 | char * strdup(const char * s) /* make a duplicate of s */ 16 | { 17 | char *p; 18 | p = (char *) malloc(strlen(s)+1); /* +1 for ’\0’ */ 19 | if (p != NULL) 20 | strcpy(p, s); 21 | return p; 22 | } 23 | 24 | void init_entry(struct nlist * entry) 25 | { 26 | entry->ptr_f32 = NULL; 27 | entry->ptr_u8 = NULL; 28 | entry->ptr_u16 = NULL; 29 | entry->ptr_u32 = NULL; 30 | entry->ptr_i8 = NULL; 31 | entry->ptr_i16 = NULL; 32 | entry->ptr_i32 = NULL; 33 | entry->ptr_function = NULL; 34 | } 35 | 36 | void init_table(struct nlist ** hashtab) 37 | { 38 | uint32_t i = 0; 39 | for(i = 0 ; i < HASHSIZE ; i++) 40 | { 41 | hashtab[i] = NULL; 42 | } 43 | } 44 | 45 | /* lookup: look for s in hashtab */ 46 | struct nlist *lookup(struct nlist ** hashtab, const char * key) 47 | { 48 | struct nlist *np; 49 | 50 | for (np = hashtab[hash(key)]; np != NULL; np = np->next) 51 | { 52 | if (strcmp(key, np->key) == 0) 53 | { 54 | return np; /* found */ 55 | } 56 | } 57 | return NULL; /* not found */ 58 | } 59 | 60 | struct nlist * install(struct nlist ** hashtab, const char * key, void * ptr, ptr_type type) 61 | { 62 | struct nlist * np; 63 | unsigned hashval; 64 | 65 | if ((np = lookup(hashtab, key)) == NULL) 66 | { 67 | // Allocate new hastable entry and initialize it 68 | np = (struct nlist *) malloc(sizeof(*np)); 69 | 70 | // If allocation failed 71 | if (np == NULL || (np->key = strdup(key)) == NULL) 72 | { 73 | free(np); 74 | return NULL; 75 | } 76 | 77 | init_entry(np); 78 | 79 | hashval = hash(key); 80 | np->next = hashtab[hashval]; 81 | hashtab[hashval] = np; 82 | } 83 | 84 | // Set value 85 | switch(type) 86 | { 87 | case ptr_f32: 88 | np->ptr_f32 = (float *)(ptr); 89 | break; 90 | case ptr_u8: 91 | np->ptr_u8 = (uint8_t *)(ptr); 92 | break; 93 | case ptr_u16: 94 | np->ptr_u16 = (uint16_t *)(ptr); 95 | break; 96 | case ptr_u32: 97 | np->ptr_u32 = (uint32_t *)(ptr); 98 | break; 99 | case ptr_i8: 100 | np->ptr_i8 = (int8_t *)(ptr); 101 | break; 102 | case ptr_i16: 103 | np->ptr_i16 = (int16_t *)(ptr); 104 | break; 105 | case ptr_i32: 106 | np->ptr_i32 = (int32_t *)(ptr); 107 | break; 108 | case ptr_function: 109 | np->ptr_function = ptr; 110 | break; 111 | } 112 | 113 | return np; 114 | } 115 | -------------------------------------------------------------------------------- /test/c/pub_sub_float_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | /// Mock of transport (serial). write is piped back to read 4 | static uint8_t endBuffer[OUTGOING_BUFFER_SIZE]; 5 | static uint32_t sizeWritten; 6 | static uint32_t sizeRead; 7 | 8 | int32_t read_float(uint8_t * buf, uint32_t sizeToRead) 9 | { 10 | int32_t rem = sizeWritten - sizeRead; 11 | uint16_t range = sizeToRead > rem ? rem : sizeToRead; 12 | int32_t i; 13 | for(i = 0 ; i < range ; i++) 14 | { 15 | buf[i] = endBuffer[sizeRead + i]; 16 | sizeRead++; 17 | } 18 | 19 | } 20 | 21 | int32_t readable_float() 22 | { 23 | return sizeWritten; 24 | } 25 | 26 | int32_t write_float(uint8_t * buf, uint32_t sizeToWrite) 27 | { 28 | sizeWritten = sizeToWrite; 29 | int32_t i; 30 | for(i = 0 ; i < sizeToWrite ; i++) 31 | { 32 | endBuffer[i] = buf[i]; 33 | } 34 | } 35 | 36 | int32_t writeable_float() 37 | { 38 | return 1; 39 | } 40 | 41 | /// end of mock 42 | 43 | struct TM_state { 44 | uint8_t called; 45 | char rcvTopic[OUTGOING_BUFFER_SIZE]; 46 | float rcvFloat; 47 | }; 48 | 49 | void callback_float(TM_state* s, TM_msg* m) 50 | { 51 | s->called = 1; 52 | float f32 = 0; 53 | strcpy(s->rcvTopic,m->topic); 54 | if(emplace_f32(m,&f32)) 55 | { 56 | s->rcvFloat = f32; 57 | } 58 | } 59 | 60 | TEST publish_float() 61 | { 62 | TM_state state; 63 | uint16_t i; 64 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 65 | { 66 | endBuffer[i] = 0; 67 | state.rcvTopic[i] = 0; 68 | } 69 | sizeWritten = 0; 70 | sizeRead = 0; 71 | state.rcvFloat = 0; 72 | state.called = 0; 73 | 74 | TM_transport transport; 75 | transport.read = read_float; 76 | transport.write = write_float; 77 | transport.readable = readable_float; 78 | transport.writeable = writeable_float; 79 | 80 | char topic[] = "topic"; 81 | float value = 1.23E4; 82 | 83 | init_telemetry(&transport); 84 | 85 | subscribe(callback_float,&state); 86 | 87 | publish_f32(topic, value); 88 | 89 | update_telemetry(); 90 | 91 | ASSERT_EQ(state.called, 1); 92 | ASSERT_STR_EQ(topic,state.rcvTopic); 93 | ASSERT_EQ_FMT(value,state.rcvFloat,"%f"); 94 | 95 | PASS(); 96 | } 97 | 98 | TEST publish_float_neg() 99 | { 100 | TM_state state; 101 | uint16_t i; 102 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 103 | { 104 | endBuffer[i] = 0; 105 | state.rcvTopic[i] = 0; 106 | } 107 | sizeWritten = 0; 108 | sizeRead = 0; 109 | state.rcvFloat = 0; 110 | state.called = 0; 111 | 112 | TM_transport transport; 113 | transport.read = read_float; 114 | transport.write = write_float; 115 | transport.readable = readable_float; 116 | transport.writeable = writeable_float; 117 | 118 | char topic[] = "topic"; 119 | float value = -1.23E4; 120 | 121 | init_telemetry(&transport); 122 | 123 | subscribe(callback_float,&state); 124 | 125 | publish_f32(topic, value); 126 | 127 | update_telemetry(); 128 | 129 | ASSERT_EQ(state.called, 1); 130 | ASSERT_STR_EQ(topic,state.rcvTopic); 131 | ASSERT_EQ_FMT(value,state.rcvFloat,"%f"); 132 | 133 | PASS(); 134 | } 135 | 136 | SUITE(pub_sub_float_suite) { 137 | RUN_TEST(publish_float); 138 | RUN_TEST(publish_float_neg); 139 | } 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Join the chat at https://gitter.im/Overdrivr/pytelemetry](https://badges.gitter.im/Overdrivr/pytelemetry.svg)](https://gitter.im/Overdrivr/pytelemetry?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | [![Stories in Ready](https://badge.waffle.io/Overdrivr/pytelemetrycli.svg?label=ready&title=Ready)](http://waffle.io/Overdrivr/pytelemetrycli) 3 | [![Build status](https://ci.appveyor.com/api/projects/status/bglm8olo8kp8x1wr?svg=true)](https://ci.appveyor.com/project/Overdrivr/telemetry) 4 | [![Documentation](https://readthedocs.org/projects/telemetry-docs/badge/?version=latest)](http://telemetry-docs.readthedocs.org/en/latest/) 5 | 6 | `Current status` *Paused development for now, will restart once I find better ways of managing and distributing general-purpose embedded libraries. However, library is functional good stability & test coverage.* 7 | 8 | # Overview 9 | `Telemetry` enables easy communication and data visualization between a computer and any embedded platform, like `ARM Mbed` or `Arduino`. 10 | 11 | Specifically, `Telemetry` is a communication protocol, implemented in C language. 12 | 13 | ![Overview](https://raw.githubusercontent.com/Overdrivr/Telemetry/master/pubsub_overview.png) 14 | 15 | Data is exchanged on named channels, called *topics* (ex : `foo`, `bar` and `qux` on the figure above). 16 | 17 | Sending data is called *publishing*. 18 | 19 | ```cpp 20 | Telemetry TM; 21 | int32_t i = 123; 22 | 23 | TM.pub_i32("foo", i); 24 | ``` 25 | 26 | For receiving data, `Telemetry` lets you attach variables and functions to topics. 27 | When fresh data is received under a topic, attached variables will be updated and attached functions will be called. 28 | 29 | ```cpp 30 | Telemetry TM; 31 | float thr; 32 | 33 | TM.attach_f32_to("throttle", &thr); 34 | 35 | for(;;) { 36 | TM.update(); 37 | } 38 | ``` 39 | 40 | # Wrappers 41 | 42 | Telemetry is written to be portable and general-purpose, and the core code is not tied to any hardware. 43 | 44 | Wrappers for specific platforms are written to provide a plug-and-play manner to use Telemetry: 45 | * [Arduino wrapper](https://github.com/Overdrivr/Telemetry-arduino) 46 | * [Mbed wrapper](https://github.com/Overdrivr/Telemetry-mbed) (Available also on [Mbed repository](https://developer.mbed.org/users/Overdrivr/code/telemetry/) ) 47 | * Yotta (package manager) - in process 48 | 49 | # Data visualization 50 | 51 | As soon as a device publishes data, it is possible to leverage the power of 52 | the [Pytelemetry Command Line Interface](https://github.com/Overdrivr/pytelemetrycli) 53 | [![PyPI version](https://badge.fury.io/py/pytelemetrycli.svg)](https://badge.fury.io/py/pytelemetrycli). 54 | 55 | This terminal application lets you interact with the device, using simple commands. 56 | 57 | Opening a live plot is as simple as 58 | 59 | ``` 60 | :> plot someTopic 61 | ``` 62 | 63 | ![Plot example](https://raw.githubusercontent.com/Overdrivr/pytelemetrycli/master/graph.png) 64 | 65 | 66 | # Central documentation 67 | 68 | * [Overview of the library](https://github.com/Overdrivr/Telemetry/wiki/Overview) 69 | * [Protocol description](https://github.com/Overdrivr/Telemetry/wiki/Protocol-description) 70 | * [A non-exhaustive list of all the awesome features](https://github.com/Overdrivr/Telemetry/wiki/Awesome-features-overview) 71 | 72 | All the information can be found from the [Wiki Home](https://github.com/Overdrivr/Telemetry/wiki). 73 | -------------------------------------------------------------------------------- /src/telemetry/c/telemetry_utils.c: -------------------------------------------------------------------------------- 1 | #include "telemetry_utils.h" 2 | 3 | uint32_t emplace(TM_msg* m, char * buf, size_t bufSize) 4 | { 5 | if(m->type != TM_string) 6 | return 0; 7 | 8 | uint32_t size = m->size; 9 | 10 | if(bufSize - 1 < size) 11 | size = bufSize - 1; 12 | 13 | strncpy(buf, (char*)(m->buffer), size); 14 | buf[size] = '\0'; 15 | 16 | return 1; 17 | } 18 | 19 | uint32_t emplace_u8(TM_msg* m, uint8_t* dst) 20 | { 21 | if(m->type != TM_uint8) 22 | return 0; 23 | 24 | memcpy(dst,m->buffer,1); 25 | return 1; 26 | } 27 | 28 | uint32_t emplace_u16(TM_msg* m, uint16_t* dst) 29 | { 30 | if(m->type != TM_uint16) 31 | return 0; 32 | 33 | memcpy(dst,m->buffer,2); 34 | return 1; 35 | } 36 | 37 | uint32_t emplace_u32(TM_msg* m, uint32_t* dst) 38 | { 39 | if(m->type != TM_uint32) 40 | return 0; 41 | 42 | memcpy(dst,m->buffer,4); 43 | return 1; 44 | } 45 | 46 | uint32_t emplace_i8(TM_msg* m, int8_t* dst) 47 | { 48 | if(m->type != TM_int8) 49 | return 0; 50 | 51 | memcpy(dst,m->buffer,1); 52 | return 1; 53 | } 54 | 55 | uint32_t emplace_i16(TM_msg* m, int16_t* dst) 56 | { 57 | if(m->type != TM_int16) 58 | return 0; 59 | 60 | memcpy(dst,m->buffer,2); 61 | return 1; 62 | } 63 | 64 | uint32_t emplace_i32(TM_msg* m, int32_t* dst) 65 | { 66 | if(m->type != TM_int32) 67 | return 0; 68 | 69 | memcpy(dst,m->buffer,4); 70 | return 1; 71 | } 72 | 73 | uint32_t emplace_f32(TM_msg* m, float* dst) 74 | { 75 | if(m->type != TM_float32) 76 | return 0; 77 | 78 | memcpy(dst,m->buffer,4); 79 | return 1; 80 | } 81 | 82 | uint32_t match(TM_msg * m, const char * topicToMatch) 83 | { 84 | if(strcmp(m->topic,topicToMatch) == 0) 85 | return 1; 86 | 87 | return 0; 88 | } 89 | 90 | uint32_t fullmatch(TM_msg * m, const char * topicToMatch, TM_type typeToMatch) 91 | { 92 | if(strcmp(m->topic,topicToMatch) == 0 && m->type == typeToMatch) 93 | return 1; 94 | 95 | return 0; 96 | } 97 | 98 | uint32_t update(TM_msg * msg, const char *topic, char *var, size_t bufSize) 99 | { 100 | if(strcmp(topic,msg->topic) == 0) 101 | return emplace(msg, var, bufSize); 102 | 103 | return 0; 104 | } 105 | 106 | uint32_t update_u8(TM_msg * msg, const char *topic, uint8_t *var) 107 | { 108 | if(strcmp(topic,msg->topic) == 0) 109 | return emplace_u8(msg, var); 110 | 111 | return 0; 112 | } 113 | 114 | uint32_t update_u16(TM_msg * msg, const char *topic, uint16_t *var) 115 | { 116 | if(strcmp(topic,msg->topic) == 0) 117 | return emplace_u16(msg, var); 118 | 119 | return 0; 120 | } 121 | 122 | uint32_t update_u32(TM_msg * msg, const char *topic, uint32_t *var) 123 | { 124 | if(strcmp(topic,msg->topic) == 0) 125 | return emplace_u32(msg, var); 126 | 127 | return 0; 128 | } 129 | 130 | uint32_t update_i8(TM_msg * msg, const char *topic, int8_t *var) 131 | { 132 | if(strcmp(topic,msg->topic) == 0) 133 | return emplace_i8(msg, var); 134 | 135 | return 0; 136 | } 137 | 138 | uint32_t update_i16(TM_msg * msg, const char *topic, int16_t *var) 139 | { 140 | if(strcmp(topic,msg->topic) == 0) 141 | return emplace_i16(msg, var); 142 | 143 | return 0; 144 | } 145 | 146 | uint32_t update_i32(TM_msg * msg, const char *topic, int32_t *var) 147 | { 148 | if(strcmp(topic,msg->topic) == 0) 149 | return emplace_i32(msg, var); 150 | 151 | return 0; 152 | } 153 | 154 | uint32_t update_f32(TM_msg * msg, const char *topic, float *var) 155 | { 156 | if(strcmp(topic,msg->topic) == 0) 157 | return emplace_f32(msg, var); 158 | 159 | return 0; 160 | } 161 | -------------------------------------------------------------------------------- /test/c/attach_test.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "dictionnary.h" 3 | 4 | /// Mock of transport (serial). write is piped back to read 5 | static uint8_t endBuffer[OUTGOING_BUFFER_SIZE]; 6 | static uint32_t sizeWritten; 7 | static uint32_t sizeRead; 8 | 9 | int32_t read(uint8_t * buf, uint32_t sizeToRead) 10 | { 11 | int32_t rem = sizeWritten - sizeRead; 12 | uint16_t range = sizeToRead > rem ? rem : sizeToRead; 13 | int32_t i; 14 | for(i = 0 ; i < range ; i++) 15 | { 16 | buf[i] = endBuffer[sizeRead + i]; 17 | sizeRead++; 18 | } 19 | 20 | } 21 | 22 | int32_t readable() 23 | { 24 | return sizeWritten; 25 | } 26 | 27 | int32_t write(uint8_t * buf, uint32_t sizeToWrite) 28 | { 29 | sizeWritten = sizeToWrite; 30 | int32_t i; 31 | for(i = 0 ; i < sizeToWrite ; i++) 32 | { 33 | endBuffer[i] = buf[i]; 34 | } 35 | } 36 | 37 | int32_t writeable() 38 | { 39 | return 1; 40 | } 41 | 42 | // Actual tests 43 | 44 | TEST test_attach_all_types() 45 | { 46 | TM_transport transport; 47 | transport.read = read; 48 | transport.write = write; 49 | transport.readable = readable; 50 | transport.writeable = writeable; 51 | 52 | init_telemetry(&transport); 53 | 54 | // Test attach float to topic foo 55 | float value = 1.23e4; 56 | 57 | attach_f32("foo",&value); 58 | publish_f32("foo", 0.f); 59 | if((value - 1.23e4) > 0.001) 60 | { 61 | FAIL(); 62 | } 63 | update_telemetry(); 64 | if((value - 0) > 0.001) 65 | { 66 | FAIL(); 67 | } 68 | 69 | // reset buffers 70 | sizeWritten = 0; 71 | sizeRead = 0; 72 | 73 | // Test attach float to topic bar 74 | float value2 = 5.67e8; 75 | 76 | attach_f32("bar",&value2); 77 | publish_f32("bar", 1.23); 78 | if((value - 5.67e8) > 0.001) 79 | { 80 | FAIL(); 81 | } 82 | update_telemetry(); 83 | if((value - 1.23) > 0.001) 84 | { 85 | FAIL(); 86 | } 87 | 88 | // reset buffers 89 | sizeWritten = 0; 90 | sizeRead = 0; 91 | 92 | // Test attach uint8 to topic foo 93 | uint8_t value_u8 = 255; 94 | 95 | attach_u8("foo",&value_u8); 96 | publish_u8("foo", 127); 97 | ASSERT_EQ_FMT(255, value_u8, "%d"); 98 | update_telemetry(); 99 | ASSERT_EQ_FMT(127, value_u8, "%d"); 100 | 101 | // reset buffers 102 | sizeWritten = 0; 103 | sizeRead = 0; 104 | 105 | uint16_t value_u16 = 65535; 106 | 107 | attach_u16("qux",&value_u16); 108 | publish_u16("qux", 127); 109 | ASSERT_EQ_FMT(65535, value_u16, "%d"); 110 | update_telemetry(); 111 | ASSERT_EQ_FMT(127, value_u16, "%d"); 112 | 113 | // reset buffers 114 | sizeWritten = 0; 115 | sizeRead = 0; 116 | 117 | uint32_t value_u32 = 4294967295; 118 | 119 | attach_u32("qux",&value_u32); 120 | publish_u32("qux", 0); 121 | ASSERT_EQ_FMT(4294967295, value_u32, "%d"); 122 | update_telemetry(); 123 | ASSERT_EQ_FMT(0, value_u32, "%d"); 124 | 125 | // reset buffers 126 | sizeWritten = 0; 127 | sizeRead = 0; 128 | 129 | int8_t value_i8 = -127; 130 | 131 | attach_i8("foo",&value_i8); 132 | publish_i8("foo", 127); 133 | ASSERT_EQ_FMT(-127, value_i8, "%d"); 134 | update_telemetry(); 135 | ASSERT_EQ_FMT(127, value_i8, "%d"); 136 | 137 | // reset buffers 138 | sizeWritten = 0; 139 | sizeRead = 0; 140 | 141 | // Test attach uint8 to topic foo 142 | int16_t value_i16 = 32767; 143 | 144 | attach_i16("qux",&value_i16); 145 | publish_i16("qux", -32767); 146 | ASSERT_EQ_FMT(32767, value_i16, "%d"); 147 | update_telemetry(); 148 | ASSERT_EQ_FMT(-32767, value_i16, "%d"); 149 | 150 | // reset buffers 151 | sizeWritten = 0; 152 | sizeRead = 0; 153 | 154 | // Test attach uint8 to topic foo 155 | int32_t value_i32 = 2147483647; 156 | 157 | attach_i32("qux",&value_i32); 158 | publish_i32("qux", -2147483647); 159 | ASSERT_EQ_FMT(2147483647, value_i32, "%d"); 160 | update_telemetry(); 161 | ASSERT_EQ_FMT(-2147483647, value_i32, "%d"); 162 | 163 | PASS(); 164 | } 165 | 166 | SUITE(attach_suite) { 167 | RUN_TEST(test_attach_all_types); 168 | } 169 | -------------------------------------------------------------------------------- /src/telemetry/c/framing.c: -------------------------------------------------------------------------------- 1 | #include "framing.h" 2 | 3 | typedef enum _state 4 | { 5 | IDLE, // No incoming frame is in process 6 | ESCAPING, // incoming frame in process, next character to be escaped 7 | ACTIVE // frame in process 8 | } _state ; 9 | 10 | typedef struct storage storage; 11 | struct storage 12 | { 13 | uint8_t * ptr; 14 | uint32_t size; 15 | uint32_t cursor; 16 | 17 | }; 18 | 19 | static storage incomingStorage; 20 | static storage outgoingStorage; 21 | 22 | int8_t safe_append(storage * s, uint8_t byte); 23 | 24 | static uint8_t SOF_; 25 | static uint8_t EOF_; 26 | static uint8_t ESC_; 27 | 28 | static _state incoming_state; 29 | 30 | void (*on_incoming_frame_cb)(uint8_t * storage, uint32_t occupiedSize); 31 | void (*on_error_cb)(int32_t errCode); 32 | 33 | void initialize_framing() 34 | { 35 | incomingStorage.ptr = NULL; 36 | outgoingStorage.ptr = NULL; 37 | 38 | incomingStorage.size = 0; 39 | outgoingStorage.size = 0; 40 | 41 | incomingStorage.cursor = 0; 42 | 43 | SOF_ = 0xF7; 44 | EOF_ = 0x7F; 45 | ESC_ = 0x7D; 46 | 47 | incoming_state = IDLE; 48 | } 49 | 50 | void outgoing_storage(uint8_t * buf, uint32_t bufSize) 51 | { 52 | outgoingStorage.ptr = buf; 53 | outgoingStorage.size = bufSize; 54 | } 55 | 56 | void begin_frame() 57 | { 58 | if(outgoingStorage.size == 0 || outgoingStorage.ptr == NULL) 59 | return; 60 | 61 | outgoingStorage.cursor = 0; 62 | 63 | // Should not fail 64 | safe_append(&outgoingStorage,SOF_); 65 | } 66 | 67 | void append(uint8_t byte) 68 | { 69 | if(outgoingStorage.size == 0 || outgoingStorage.ptr == NULL) 70 | return; 71 | 72 | // byte == to flag, need to escape it 73 | if(byte == SOF_ || byte == EOF_ || byte == ESC_) 74 | { 75 | if(!safe_append(&outgoingStorage,ESC_)) 76 | return; 77 | } 78 | 79 | if(!safe_append(&outgoingStorage,byte)) 80 | return; 81 | } 82 | 83 | void append2(uint16_t twobytes) 84 | { 85 | uint8_t * ptr = (uint8_t*)(&twobytes); 86 | append(ptr[0]); 87 | append(ptr[1]); 88 | } 89 | 90 | void append4(uint32_t fourbytes) 91 | { 92 | uint8_t * ptr = (uint8_t*)(&fourbytes); 93 | append(ptr[0]); 94 | append(ptr[1]); 95 | append(ptr[2]); 96 | append(ptr[3]); 97 | } 98 | 99 | uint32_t end_frame() 100 | { 101 | if(outgoingStorage.size == 0 || outgoingStorage.ptr == NULL) 102 | return 0; 103 | 104 | if(!safe_append(&outgoingStorage,EOF_)) 105 | return 0; 106 | 107 | return outgoingStorage.cursor; 108 | } 109 | 110 | void incoming_storage(uint8_t * buf, uint32_t bufSize) 111 | { 112 | incomingStorage.ptr = buf; 113 | incomingStorage.size = bufSize; 114 | } 115 | 116 | void set_on_incoming_frame(void (*callback)(uint8_t * storage, uint32_t occupiedSize)) 117 | { 118 | on_incoming_frame_cb = callback; 119 | } 120 | 121 | void set_on_incoming_error(void (*callback)(int32_t errCode)) 122 | { 123 | on_error_cb = callback; 124 | } 125 | 126 | void feed(uint8_t byte) 127 | { 128 | if(incomingStorage.size == 0 || incomingStorage.ptr == NULL) 129 | return; 130 | 131 | if(incoming_state == ESCAPING) 132 | { 133 | if(!safe_append(&incomingStorage,byte)) 134 | { 135 | incoming_state = IDLE; 136 | return; 137 | } 138 | incoming_state = ACTIVE; 139 | return; 140 | } 141 | 142 | if(byte == SOF_) 143 | { 144 | incoming_state = ACTIVE; 145 | incomingStorage.cursor = 0; 146 | return; 147 | } 148 | 149 | if(incoming_state == ACTIVE) 150 | { 151 | if(byte == EOF_) 152 | { 153 | incoming_state = IDLE; 154 | on_incoming_frame_cb(incomingStorage.ptr, incomingStorage.cursor); 155 | } 156 | // Escape next character 157 | else if(byte == ESC_) 158 | { 159 | incoming_state = ESCAPING; 160 | } 161 | else 162 | { 163 | if(!safe_append(&incomingStorage,byte)) 164 | { 165 | incoming_state = IDLE; 166 | return; 167 | } 168 | incoming_state = ACTIVE; 169 | } 170 | } 171 | } 172 | 173 | int8_t safe_append(storage * s, uint8_t byte) 174 | { 175 | // Not enough space for 1 more character 176 | if(s->cursor + 1 >= s->size) 177 | return 0; 178 | 179 | s->ptr[s->cursor++] = byte; 180 | 181 | return 1; 182 | } 183 | -------------------------------------------------------------------------------- /test/c/framing_incoming_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "framing.h" 3 | 4 | static uint8_t rcv_data[12]; 5 | static uint32_t rcv_size; 6 | static uint8_t complete; 7 | 8 | void callback_incoming(uint8_t * storage, uint32_t occupiedSize) 9 | { 10 | complete = 1; 11 | uint32_t amount = occupiedSize > 12 ? 12 : occupiedSize; 12 | rcv_size = amount; 13 | memcpy(rcv_data, storage, amount); 14 | } 15 | 16 | TEST framing_simple_feed() 17 | { 18 | uint8_t incomingBuffer[12] = {0}; 19 | complete = 0; 20 | rcv_size = 0; 21 | uint32_t i; 22 | for(i = 0 ; i < 12 ; i++) 23 | rcv_data[i] = 0; 24 | 25 | initialize_framing(); 26 | incoming_storage(incomingBuffer,12); 27 | set_on_incoming_frame(callback_incoming); 28 | 29 | uint8_t feed_data[] = {0xF7,0xFF,0x7F}; 30 | 31 | for(i = 0 ; i < 3 ; i++) 32 | { 33 | feed(feed_data[i]); 34 | } 35 | 36 | ASSERTm("Valid frame was not detected. callback not triggered.", complete); 37 | ASSERT_EQ_FMT(feed_data[1],rcv_data[0],"%d"); 38 | ASSERT_EQ_FMT(1,rcv_size,"%d"); 39 | 40 | PASS(); 41 | } 42 | 43 | TEST framing_corrupted_then_valid_feed() 44 | { 45 | uint8_t incomingBuffer[12] = {0}; 46 | complete = 0; 47 | rcv_size = 0; 48 | uint32_t i; 49 | for(i = 0 ; i < 12 ; i++) 50 | rcv_data[i] = 0; 51 | 52 | initialize_framing(); 53 | incoming_storage(incomingBuffer,12); 54 | set_on_incoming_frame(callback_incoming); 55 | 56 | uint8_t feed_data[] = {0xF7,0xFF,0xF7,0xDD,0x7F}; 57 | 58 | for(i = 0 ; i < 5 ; i++) 59 | { 60 | feed(feed_data[i]); 61 | } 62 | 63 | ASSERTm("Valid frame was not detected. callback not triggered.", complete); 64 | ASSERT_EQ_FMT(1,rcv_size,"%d"); 65 | ASSERT_EQ_FMT(feed_data[3],rcv_data[0],"%d"); 66 | 67 | PASS(); 68 | } 69 | 70 | TEST framing_basic_escaping_feed() 71 | { 72 | uint8_t incomingBuffer[12] = {0}; 73 | complete = 0; 74 | rcv_size = 0; 75 | uint32_t i; 76 | 77 | for(i = 0 ; i < 12 ; i++) 78 | rcv_data[i] = 0; 79 | 80 | initialize_framing(); 81 | incoming_storage(incomingBuffer,12); 82 | set_on_incoming_frame(callback_incoming); 83 | 84 | uint8_t feed_data[] = {0xF7,0x7D,0xFF,0xDD,0x7F}; // useless ESC for test only 85 | 86 | for(i = 0 ; i < 5 ; i++) 87 | { 88 | feed(feed_data[i]); 89 | } 90 | 91 | ASSERTm("Valid frame was not detected. callback not triggered.", complete); 92 | ASSERT_EQ_FMT(2,rcv_size,"%d"); 93 | ASSERT_EQ_FMT(feed_data[2],rcv_data[0],"%d"); 94 | ASSERT_EQ_FMT(feed_data[3],rcv_data[1],"%d"); 95 | 96 | PASS(); 97 | } 98 | 99 | TEST framing_all_escaped_flags_feed() 100 | { 101 | uint8_t incomingBuffer[12] = {0}; 102 | complete = 0; 103 | rcv_size = 0; 104 | uint32_t i; 105 | 106 | for(i = 0 ; i < 12 ; i++) 107 | rcv_data[i] = 0; 108 | 109 | initialize_framing(); 110 | incoming_storage(incomingBuffer,12); 111 | set_on_incoming_frame(callback_incoming); 112 | 113 | uint8_t feed_data[] = {0xF7,0x7D,0x7F,0x7D,0xF7,0x7D,0x7D,0x7F}; 114 | 115 | for(i = 0 ; i < 8 ; i++) 116 | { 117 | feed(feed_data[i]); 118 | } 119 | 120 | ASSERTm("Valid frame was not detected. callback not triggered.", complete); 121 | ASSERT_EQ_FMT(3,rcv_size,"%d"); 122 | ASSERT_EQ_FMT(feed_data[2],rcv_data[0],"%d"); 123 | ASSERT_EQ_FMT(feed_data[4],rcv_data[1],"%d"); 124 | ASSERT_EQ_FMT(feed_data[6],rcv_data[2],"%d"); 125 | 126 | PASS(); 127 | } 128 | 129 | TEST framing_overflowing_feed() 130 | { 131 | uint8_t incomingBuffer[3] = {0}; 132 | complete = 0; 133 | rcv_size = 0; 134 | uint32_t i; 135 | 136 | for(i = 0 ; i < 12 ; i++) 137 | rcv_data[i] = 0; 138 | 139 | initialize_framing(); 140 | incoming_storage(incomingBuffer,3); 141 | set_on_incoming_frame(callback_incoming); 142 | 143 | uint8_t feed_data[] = {0xF7,0x7D,0x7F,0x7D,0xF7,0x7D,0x7D,0x7F}; 144 | 145 | for(i = 0 ; i < 8 ; i++) 146 | { 147 | feed(feed_data[i]); 148 | } 149 | 150 | ASSERT_FALSEm("Valid frame was detected while not expected to.", complete); 151 | ASSERT_EQ_FMT(0,rcv_size,"%d"); 152 | 153 | PASS(); 154 | } 155 | 156 | SUITE(framing_incoming_suite) { 157 | RUN_TEST(framing_simple_feed); 158 | RUN_TEST(framing_corrupted_then_valid_feed); 159 | RUN_TEST(framing_basic_escaping_feed); 160 | RUN_TEST(framing_all_escaped_flags_feed); 161 | RUN_TEST(framing_overflowing_feed); 162 | } 163 | -------------------------------------------------------------------------------- /test/c/pub_sub_uint_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | /// Mock of transport (serial). write is piped back to read 4 | static uint8_t endBuffer[OUTGOING_BUFFER_SIZE]; 5 | static uint32_t sizeWritten; 6 | static uint32_t sizeRead; 7 | 8 | int32_t read_uint(uint8_t * buf, uint32_t sizeToRead) 9 | { 10 | int32_t rem = sizeWritten - sizeRead; 11 | uint16_t range = sizeToRead > rem ? rem : sizeToRead; 12 | int32_t i; 13 | for(i = 0 ; i < range ; i++) 14 | { 15 | buf[i] = endBuffer[sizeRead + i]; 16 | sizeRead++; 17 | } 18 | 19 | } 20 | 21 | int32_t readable_uint() 22 | { 23 | return sizeWritten; 24 | } 25 | 26 | int32_t write_uint(uint8_t * buf, uint32_t sizeToWrite) 27 | { 28 | sizeWritten = sizeToWrite; 29 | int32_t i; 30 | for(i = 0 ; i < sizeToWrite ; i++) 31 | { 32 | endBuffer[i] = buf[i]; 33 | } 34 | } 35 | 36 | int32_t writeable_uint() 37 | { 38 | return 1; 39 | } 40 | 41 | /// end of mock 42 | 43 | struct TM_state { 44 | uint8_t called; 45 | char rcvTopic[OUTGOING_BUFFER_SIZE]; 46 | uint8_t rcvUint8; 47 | uint16_t rcvUint16; 48 | uint32_t rcvUint32; 49 | }; 50 | 51 | void callback_uint(TM_state* s, TM_msg* m) 52 | { 53 | s->called = 1; 54 | uint8_t u8 = 0; 55 | uint16_t u16 = 0; 56 | uint32_t u32 = 0; 57 | strcpy(s->rcvTopic,m->topic); 58 | if(emplace_u8(m,&u8)) 59 | { 60 | s->rcvUint8 = u8; 61 | } 62 | if(emplace_u16(m,&u16)) 63 | { 64 | s->rcvUint16 = u16; 65 | } 66 | if(emplace_u32(m,&u32)) 67 | { 68 | s->rcvUint32 = u32; 69 | } 70 | } 71 | 72 | TEST publish_uint8() 73 | { 74 | TM_state state; 75 | uint16_t i; 76 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 77 | { 78 | endBuffer[i] = 0; 79 | state.rcvTopic[i] = 0; 80 | } 81 | sizeWritten = 0; 82 | sizeRead = 0; 83 | state.rcvUint8 = 0; 84 | state.called = 0; 85 | 86 | TM_transport transport; 87 | transport.read = read_uint; 88 | transport.write = write_uint; 89 | transport.readable = readable_uint; 90 | transport.writeable = writeable_uint; 91 | 92 | char topic[] = "topic"; 93 | uint8_t value = 255; 94 | 95 | init_telemetry(&transport); 96 | 97 | subscribe(callback_uint,&state); 98 | 99 | publish_u8(topic, value); 100 | 101 | update_telemetry(); 102 | 103 | ASSERT_EQ(state.called, 1); 104 | ASSERT_STR_EQ(topic,state.rcvTopic); 105 | ASSERT_EQ_FMT(value,state.rcvUint8,"%d"); 106 | 107 | PASS(); 108 | } 109 | 110 | TEST publish_uint16() 111 | { 112 | TM_state state; 113 | uint16_t i; 114 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 115 | { 116 | endBuffer[i] = 0; 117 | state.rcvTopic[i] = 0; 118 | } 119 | sizeWritten = 0; 120 | sizeRead = 0; 121 | state.rcvUint16 = 0; 122 | state.called = 0; 123 | 124 | TM_transport transport; 125 | transport.read = read_uint; 126 | transport.write = write_uint; 127 | transport.readable = readable_uint; 128 | transport.writeable = writeable_uint; 129 | 130 | char topic[] = "topic"; 131 | uint16_t value = 65535; 132 | 133 | init_telemetry(&transport); 134 | 135 | subscribe(callback_uint,&state); 136 | 137 | publish_u16(topic, value); 138 | 139 | update_telemetry(); 140 | 141 | ASSERT_EQ(state.called, 1); 142 | ASSERT_STR_EQ(topic,state.rcvTopic); 143 | ASSERT_EQ_FMT(value,state.rcvUint16,"%d"); 144 | 145 | PASS(); 146 | } 147 | 148 | TEST publish_uint32() 149 | { 150 | TM_state state; 151 | uint16_t i; 152 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 153 | { 154 | endBuffer[i] = 0; 155 | state.rcvTopic[i] = 0; 156 | } 157 | sizeWritten = 0; 158 | sizeRead = 0; 159 | state.rcvUint32 = 0; 160 | state.called = 0; 161 | 162 | TM_transport transport; 163 | transport.read = read_uint; 164 | transport.write = write_uint; 165 | transport.readable = readable_uint; 166 | transport.writeable = writeable_uint; 167 | 168 | char topic[] = "topic"; 169 | uint32_t value = 4294967295; 170 | 171 | init_telemetry(&transport); 172 | 173 | subscribe(callback_uint,&state); 174 | 175 | publish_u32(topic, value); 176 | 177 | update_telemetry(); 178 | 179 | ASSERT_EQ(state.called, 1); 180 | ASSERT_STR_EQ(topic,state.rcvTopic); 181 | ASSERT_EQ_FMT(value,state.rcvUint32,"%d"); 182 | 183 | PASS(); 184 | } 185 | 186 | SUITE(pub_sub_uint_suite) { 187 | RUN_TEST(publish_uint8); 188 | RUN_TEST(publish_uint16); 189 | RUN_TEST(publish_uint32); 190 | } 191 | -------------------------------------------------------------------------------- /tools/testvectorsgenerator/c/main.c: -------------------------------------------------------------------------------- 1 | // An executable file that generates two different test vectors 2 | // shouldpass.md contains a dictionnary of datatype/topic/value -> frame 3 | // that should be identified successfully by any distribution 4 | // shoudlfail.md contains a set of non valid frames 5 | 6 | #include "telemetry_core.h" 7 | #include "stdio.h" 8 | 9 | FILE * output; 10 | 11 | // Not used. Don't care 12 | int32_t read(void * buf, uint32_t sizeToRead) 13 | { 14 | return 0; 15 | } 16 | 17 | // Not used. Don't care 18 | int32_t readable() 19 | { 20 | return 0; 21 | } 22 | 23 | int32_t write(void * buf, uint32_t sizeToWrite) 24 | { 25 | uint8_t * caster = (uint8_t*)(buf); 26 | uint32_t i; 27 | for(i = 0 ; i < sizeToWrite ; i++) 28 | { 29 | fprintf(output, "%02x", caster[i]); 30 | } 31 | } 32 | 33 | int32_t writeable() 34 | { 35 | return 1; 36 | } 37 | 38 | void newentry(FILE * file, const char * topic, const char * msg) 39 | { 40 | fputs("string", file); 41 | fputs("\t;\t", file); 42 | fputs(topic, file); 43 | fputs("\t;\t", file); 44 | fputs(msg, file); 45 | fputs("\t;\t", file); 46 | publish(topic,msg); 47 | fputs("\n", file); 48 | } 49 | 50 | void newentry_u8(FILE * file, const char * topic, uint8_t data) 51 | { 52 | fputs("u8", file); 53 | fputs("\t;\t", file); 54 | fputs(topic, file); 55 | fputs("\t;\t", file); 56 | fprintf(file, "%hhu", data); 57 | fputs("\t;\t", file); 58 | publish_u8(topic,data); 59 | fputs("\n", file); 60 | } 61 | 62 | void newentry_u16(FILE * file, const char * topic, uint16_t data) 63 | { 64 | fputs("u16", file); 65 | fputs("\t;\t", file); 66 | fputs(topic, file); 67 | fputs("\t;\t", file); 68 | fprintf(file, "%hu", data); 69 | fputs("\t;\t", file); 70 | publish_u16(topic,data); 71 | fputs("\n", file); 72 | } 73 | 74 | void newentry_u32(FILE * file, const char * topic, uint32_t data) 75 | { 76 | fputs("u32", file); 77 | fputs("\t;\t", file); 78 | fputs(topic, file); 79 | fputs("\t;\t", file); 80 | fprintf(file, "%u", data); 81 | fputs("\t;\t", file); 82 | publish_u32(topic,data); 83 | fputs("\n", file); 84 | } 85 | 86 | void newentry_i8(FILE * file, const char * topic, int8_t data) 87 | { 88 | fputs("i8", file); 89 | fputs("\t;\t", file); 90 | fputs(topic, file); 91 | fputs("\t;\t", file); 92 | fprintf(file, "%hhi", data); 93 | fputs("\t;\t", file); 94 | publish_i8(topic,data); 95 | fputs("\n", file); 96 | } 97 | 98 | void newentry_i16(FILE * file, const char * topic, int16_t data) 99 | { 100 | fputs("i16", file); 101 | fputs("\t;\t", file); 102 | fputs(topic, file); 103 | fputs("\t;\t", file); 104 | fprintf(file, "%hi", data); 105 | fputs("\t;\t", file); 106 | publish_i16(topic,data); 107 | fputs("\n", file); 108 | } 109 | 110 | void newentry_i32(FILE * file, const char * topic, int32_t data) 111 | { 112 | fputs("i32", file); 113 | fputs("\t;\t", file); 114 | fputs(topic, file); 115 | fputs("\t;\t", file); 116 | fprintf(file, "%i", data); 117 | fputs("\t;\t", file); 118 | publish_i32(topic,data); 119 | fputs("\n", file); 120 | } 121 | 122 | void newentry_f32(FILE * file, const char * topic, float data) 123 | { 124 | fputs("f32", file); 125 | fputs("\t;\t", file); 126 | fputs(topic, file); 127 | fputs("\t;\t", file); 128 | fprintf(file, "%f", data); 129 | fputs("\t;\t", file); 130 | publish_f32(topic,data); 131 | fputs("\n", file); 132 | } 133 | 134 | int main() 135 | { 136 | TM_transport transport; 137 | 138 | transport.write = write; 139 | transport.read = read; 140 | transport.readable = readable; 141 | transport.writeable = writeable; 142 | 143 | init_telemetry(&transport); 144 | 145 | output = NULL; 146 | output = fopen("valid_vectors.csv","w"); 147 | if(output == NULL) 148 | { 149 | printf("Failure : Could not open file for write."); 150 | return -1; 151 | } 152 | 153 | // Test vectors 154 | newentry(output, "foo" ,"bar"); 155 | newentry(output, "foo with spaces" ,"bar with spaces"); 156 | newentry(output, "0123456789" ,"0123456789"); 157 | newentry(output, "ABCD/EFGH:IJK" ,"abcdefghijk"); 158 | newentry(output, "lmnopqrstuv:123" ,"wxyz"); 159 | 160 | newentry_u8(output, "bar", 0); 161 | newentry_u8(output, "foo", 255); 162 | newentry_u8(output, "hello world", 127); 163 | 164 | newentry_u16(output, "abcdef", 0); 165 | newentry_u16(output, "bar", 65535); 166 | newentry_u16(output, "fooqux", 256); 167 | 168 | newentry_u32(output, "ghij", 0); 169 | newentry_u32(output, "test", 1); 170 | newentry_u32(output, "klmopq", 4294967295); 171 | newentry_u32(output, "rstuv", 65536); 172 | 173 | newentry_i8(output, "bar", 0); 174 | newentry_i8(output, "plus", 1); 175 | newentry_i8(output, "minus", -1); 176 | newentry_i8(output, "foo", -127); 177 | newentry_i8(output, "hello world", -127); 178 | 179 | newentry_i16(output, "abcdef", 0); 180 | newentry_i16(output, "minus", -1); 181 | newentry_i16(output, "plus", 1); 182 | newentry_i16(output, "bar", 32767); 183 | newentry_i16(output, "fooqux", -32767); 184 | 185 | newentry_i32(output, "ghij", 0); 186 | newentry_i32(output, "minus", -1); 187 | newentry_i32(output, "plus", 1); 188 | newentry_i32(output, "klmopq", 2147483647); 189 | newentry_i32(output, "rstuv", -2147483648); 190 | 191 | newentry_f32(output, "zeropos", 0.f); 192 | newentry_f32(output, "zeropos", -0.f); 193 | newentry_f32(output, "normally perfect", 61.f); 194 | newentry_f32(output, "other", 1.2e34); 195 | newentry_f32(output, "otherneg", -1.2e34); 196 | 197 | close(output); 198 | printf("Valid test vector generation success.\n"); 199 | return 0; 200 | } 201 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /test/c/dictionnary_test.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "dictionnary.h" 3 | 4 | TEST dictionnary_insert_f32() 5 | { 6 | struct nlist * hashtab[HASHSIZE]; 7 | init_table(hashtab); 8 | 9 | float testvar = 0.f; 10 | 11 | install(hashtab, "foo", (void*)(&testvar), ptr_f32); 12 | 13 | // Test installed structure can be retrieved 14 | struct nlist * np = lookup(hashtab, "foo"); 15 | if(np == NULL) 16 | { 17 | FAIL(); 18 | } 19 | 20 | ASSERT_EQ(np->ptr_f32, &testvar); 21 | 22 | ASSERT_EQ(np->ptr_u8, NULL); 23 | ASSERT_EQ(np->ptr_u16, NULL); 24 | ASSERT_EQ(np->ptr_u32, NULL); 25 | ASSERT_EQ(np->ptr_i8, NULL); 26 | ASSERT_EQ(np->ptr_i16, NULL); 27 | ASSERT_EQ(np->ptr_i32, NULL); 28 | ASSERT_EQ(np->ptr_function, NULL); 29 | 30 | ASSERT_EQ(*(np->ptr_f32),testvar); 31 | 32 | PASS(); 33 | } 34 | 35 | TEST dictionnary_insert_u8() 36 | { 37 | struct nlist * hashtab[HASHSIZE]; 38 | init_table(hashtab); 39 | 40 | uint8_t testvar = 255; 41 | 42 | install(hashtab, "bar", (void*)(&testvar), ptr_u8); 43 | 44 | // Test installed structure can be retrieved 45 | struct nlist * np = lookup(hashtab, "bar"); 46 | if(np == NULL) 47 | { 48 | FAIL(); 49 | } 50 | 51 | ASSERT_EQ(np->ptr_u8, &testvar); 52 | 53 | ASSERT_EQ(np->ptr_f32, NULL); 54 | ASSERT_EQ(np->ptr_u16, NULL); 55 | ASSERT_EQ(np->ptr_u32, NULL); 56 | ASSERT_EQ(np->ptr_i8, NULL); 57 | ASSERT_EQ(np->ptr_i16, NULL); 58 | ASSERT_EQ(np->ptr_i32, NULL); 59 | ASSERT_EQ(np->ptr_function, NULL); 60 | 61 | ASSERT_EQ(*(np->ptr_u8),testvar); 62 | 63 | PASS(); 64 | } 65 | 66 | TEST dictionnary_insert_u16() 67 | { 68 | struct nlist * hashtab[HASHSIZE]; 69 | init_table(hashtab); 70 | 71 | uint16_t testvar = 65535; 72 | 73 | install(hashtab, "bar", (void*)(&testvar), ptr_u16); 74 | 75 | // Test installed structure can be retrieved 76 | struct nlist * np = lookup(hashtab, "bar"); 77 | if(np == NULL) 78 | { 79 | FAIL(); 80 | } 81 | 82 | ASSERT_EQ(np->ptr_u16, &testvar); 83 | 84 | ASSERT_EQ(np->ptr_f32, NULL); 85 | ASSERT_EQ(np->ptr_u8, NULL); 86 | ASSERT_EQ(np->ptr_u32, NULL); 87 | ASSERT_EQ(np->ptr_i8, NULL); 88 | ASSERT_EQ(np->ptr_i16, NULL); 89 | ASSERT_EQ(np->ptr_i32, NULL); 90 | ASSERT_EQ(np->ptr_function, NULL); 91 | 92 | ASSERT_EQ(*(np->ptr_u16),testvar); 93 | 94 | PASS(); 95 | } 96 | 97 | TEST dictionnary_insert_u32() 98 | { 99 | struct nlist * hashtab[HASHSIZE]; 100 | init_table(hashtab); 101 | 102 | uint32_t testvar = 4294967295; 103 | 104 | install(hashtab, "bar", (void*)(&testvar), ptr_u32); 105 | 106 | // Test installed structure can be retrieved 107 | struct nlist * np = lookup(hashtab, "bar"); 108 | if(np == NULL) 109 | { 110 | FAIL(); 111 | } 112 | 113 | ASSERT_EQ(np->ptr_u32, &testvar); 114 | 115 | ASSERT_EQ(np->ptr_f32, NULL); 116 | ASSERT_EQ(np->ptr_u8, NULL); 117 | ASSERT_EQ(np->ptr_u16, NULL); 118 | ASSERT_EQ(np->ptr_i8, NULL); 119 | ASSERT_EQ(np->ptr_i16, NULL); 120 | ASSERT_EQ(np->ptr_i32, NULL); 121 | ASSERT_EQ(np->ptr_function, NULL); 122 | 123 | ASSERT_EQ(*(np->ptr_u32),testvar); 124 | 125 | PASS(); 126 | } 127 | 128 | TEST dictionnary_insert_i8() 129 | { 130 | struct nlist * hashtab[HASHSIZE]; 131 | init_table(hashtab); 132 | 133 | int8_t testvar = -127; 134 | 135 | install(hashtab, "bar", (void*)(&testvar), ptr_i8); 136 | 137 | // Test installed structure can be retrieved 138 | struct nlist * np = lookup(hashtab, "bar"); 139 | if(np == NULL) 140 | { 141 | FAIL(); 142 | } 143 | 144 | ASSERT_EQ(np->ptr_i8, &testvar); 145 | 146 | ASSERT_EQ(np->ptr_f32, NULL); 147 | ASSERT_EQ(np->ptr_u8, NULL); 148 | ASSERT_EQ(np->ptr_u16, NULL); 149 | ASSERT_EQ(np->ptr_u32, NULL); 150 | ASSERT_EQ(np->ptr_i16, NULL); 151 | ASSERT_EQ(np->ptr_i32, NULL); 152 | ASSERT_EQ(np->ptr_function, NULL); 153 | 154 | ASSERT_EQ(*(np->ptr_i8),testvar); 155 | 156 | PASS(); 157 | } 158 | 159 | TEST dictionnary_insert_i16() 160 | { 161 | struct nlist * hashtab[HASHSIZE]; 162 | init_table(hashtab); 163 | 164 | int16_t testvar = -32767; 165 | 166 | install(hashtab, "bar", (void*)(&testvar), ptr_i16); 167 | 168 | // Test installed structure can be retrieved 169 | struct nlist * np = lookup(hashtab, "bar"); 170 | if(np == NULL) 171 | { 172 | FAIL(); 173 | } 174 | 175 | ASSERT_EQ(np->ptr_i16, &testvar); 176 | 177 | ASSERT_EQ(np->ptr_f32, NULL); 178 | ASSERT_EQ(np->ptr_u8, NULL); 179 | ASSERT_EQ(np->ptr_u16, NULL); 180 | ASSERT_EQ(np->ptr_u32, NULL); 181 | ASSERT_EQ(np->ptr_i8, NULL); 182 | ASSERT_EQ(np->ptr_i32, NULL); 183 | ASSERT_EQ(np->ptr_function, NULL); 184 | 185 | ASSERT_EQ(*(np->ptr_i16),testvar); 186 | 187 | PASS(); 188 | } 189 | 190 | TEST dictionnary_insert_i32() 191 | { 192 | struct nlist * hashtab[HASHSIZE]; 193 | init_table(hashtab); 194 | 195 | int32_t testvar = -2147483647; 196 | 197 | install(hashtab, "bar", (void*)(&testvar), ptr_i32); 198 | 199 | // Test installed structure can be retrieved 200 | struct nlist * np = lookup(hashtab, "bar"); 201 | if(np == NULL) 202 | { 203 | FAIL(); 204 | } 205 | 206 | ASSERT_EQ(np->ptr_i32, &testvar); 207 | 208 | ASSERT_EQ(np->ptr_f32, NULL); 209 | ASSERT_EQ(np->ptr_u8, NULL); 210 | ASSERT_EQ(np->ptr_u16, NULL); 211 | ASSERT_EQ(np->ptr_u32, NULL); 212 | ASSERT_EQ(np->ptr_i8, NULL); 213 | ASSERT_EQ(np->ptr_i16, NULL); 214 | ASSERT_EQ(np->ptr_function, NULL); 215 | 216 | ASSERT_EQ(*(np->ptr_i32),testvar); 217 | 218 | PASS(); 219 | } 220 | 221 | void callback() 222 | { 223 | 224 | } 225 | 226 | TEST dictionnary_insert_function() 227 | { 228 | struct nlist * hashtab[HASHSIZE]; 229 | init_table(hashtab); 230 | 231 | install(hashtab, "bar", (void*)(callback), ptr_function); 232 | 233 | // Test installed structure can be retrieved 234 | struct nlist * np = lookup(hashtab, "bar"); 235 | if(np == NULL) 236 | { 237 | FAIL(); 238 | } 239 | 240 | ASSERT_EQ(np->ptr_function, callback); 241 | 242 | ASSERT_EQ(np->ptr_f32, NULL); 243 | ASSERT_EQ(np->ptr_u8, NULL); 244 | ASSERT_EQ(np->ptr_u16, NULL); 245 | ASSERT_EQ(np->ptr_u32, NULL); 246 | ASSERT_EQ(np->ptr_i8, NULL); 247 | ASSERT_EQ(np->ptr_i16, NULL); 248 | ASSERT_EQ(np->ptr_i32, NULL); 249 | 250 | PASS(); 251 | } 252 | 253 | 254 | 255 | SUITE(dictionnary_suite) { 256 | RUN_TEST(dictionnary_insert_f32); 257 | RUN_TEST(dictionnary_insert_u8); 258 | RUN_TEST(dictionnary_insert_u16); 259 | RUN_TEST(dictionnary_insert_u32); 260 | RUN_TEST(dictionnary_insert_i8); 261 | RUN_TEST(dictionnary_insert_i16); 262 | RUN_TEST(dictionnary_insert_i32); 263 | RUN_TEST(dictionnary_insert_function); 264 | } 265 | -------------------------------------------------------------------------------- /test/c/pub_sub_int_suite.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | /// Mock of transport (serial). write is piped back to read 4 | static uint8_t endBuffer[OUTGOING_BUFFER_SIZE]; 5 | static uint32_t sizeWritten; 6 | static uint32_t sizeRead; 7 | 8 | int32_t read_int(uint8_t * buf, uint32_t sizeToRead) 9 | { 10 | int32_t rem = sizeWritten - sizeRead; 11 | uint16_t range = sizeToRead > rem ? rem : sizeToRead; 12 | int32_t i; 13 | for(i = 0 ; i < range ; i++) 14 | { 15 | buf[i] = endBuffer[sizeRead + i]; 16 | sizeRead++; 17 | } 18 | 19 | } 20 | 21 | int32_t readable_int() 22 | { 23 | return sizeWritten; 24 | } 25 | 26 | int32_t write_int(uint8_t * buf, uint32_t sizeToWrite) 27 | { 28 | sizeWritten = sizeToWrite; 29 | int32_t i; 30 | for(i = 0 ; i < sizeToWrite ; i++) 31 | { 32 | endBuffer[i] = buf[i]; 33 | } 34 | } 35 | 36 | int32_t writeable_int() 37 | { 38 | return 1; 39 | } 40 | 41 | /// end of mock 42 | 43 | struct TM_state { 44 | uint8_t called; 45 | char rcvTopic[OUTGOING_BUFFER_SIZE]; 46 | int8_t rcvInt8; 47 | int16_t rcvInt16; 48 | int32_t rcvInt32; 49 | }; 50 | 51 | void callback_int(TM_state* s, TM_msg* m) 52 | { 53 | s->called = 1; 54 | int8_t i8 = 0; 55 | int16_t i16 = 0; 56 | int32_t i32 = 0; 57 | strcpy(s->rcvTopic,m->topic); 58 | 59 | if(emplace_i8(m,&i8)) 60 | { 61 | s->rcvInt8 = i8; 62 | } 63 | if(emplace_i16(m,&i16)) 64 | { 65 | s->rcvInt16 = i16; 66 | } 67 | if(emplace_i32(m,&i32)) 68 | { 69 | s->rcvInt32 = i32; 70 | } 71 | } 72 | 73 | TEST publish_int8() 74 | { 75 | TM_state state; 76 | uint16_t i; 77 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 78 | { 79 | endBuffer[i] = 0; 80 | state.rcvTopic[i] = 0; 81 | } 82 | sizeWritten = 0; 83 | sizeRead = 0; 84 | state.rcvInt8 = 0; 85 | state.called = 0; 86 | 87 | TM_transport transport; 88 | transport.read = read_int; 89 | transport.write = write_int; 90 | transport.readable = readable_int; 91 | transport.writeable = writeable_int; 92 | 93 | char topic[] = "topic"; 94 | int8_t value = 127; 95 | 96 | init_telemetry(&transport); 97 | 98 | subscribe(callback_int,&state); 99 | 100 | publish_i8(topic, value); 101 | 102 | update_telemetry(); 103 | 104 | ASSERT_EQ(state.called, 1); 105 | ASSERT_STR_EQ(topic,state.rcvTopic); 106 | ASSERT_EQ_FMT(value,state.rcvInt8,"%d"); 107 | 108 | PASS(); 109 | } 110 | 111 | TEST publish_int8_neg() 112 | { 113 | TM_state state; 114 | uint16_t i; 115 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 116 | { 117 | endBuffer[i] = 0; 118 | state.rcvTopic[i] = 0; 119 | } 120 | sizeWritten = 0; 121 | sizeRead = 0; 122 | state.rcvInt8 = 0; 123 | state.called = 0; 124 | 125 | TM_transport transport; 126 | transport.read = read_int; 127 | transport.write = write_int; 128 | transport.readable = readable_int; 129 | transport.writeable = writeable_int; 130 | 131 | char topic[] = "topic"; 132 | int8_t value = -127; 133 | 134 | init_telemetry(&transport); 135 | 136 | subscribe(callback_int,&state); 137 | 138 | publish_i8(topic, value); 139 | 140 | update_telemetry(); 141 | 142 | ASSERT_EQ(state.called, 1); 143 | ASSERT_STR_EQ(topic,state.rcvTopic); 144 | ASSERT_EQ_FMT(value,state.rcvInt8,"%d"); 145 | 146 | PASS(); 147 | } 148 | 149 | TEST publish_int16() 150 | { 151 | TM_state state; 152 | uint16_t i; 153 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 154 | { 155 | endBuffer[i] = 0; 156 | state.rcvTopic[i] = 0; 157 | } 158 | sizeWritten = 0; 159 | sizeRead = 0; 160 | state.rcvInt16 = 0; 161 | state.called = 0; 162 | 163 | TM_transport transport; 164 | transport.read = read_int; 165 | transport.write = write_int; 166 | transport.readable = readable_int; 167 | transport.writeable = writeable_int; 168 | 169 | char topic[] = "topic"; 170 | int16_t value = 32767; 171 | 172 | init_telemetry(&transport); 173 | 174 | subscribe(callback_int,&state); 175 | 176 | publish_i16(topic, value); 177 | 178 | update_telemetry(); 179 | 180 | ASSERT_EQ(state.called, 1); 181 | ASSERT_STR_EQ(topic,state.rcvTopic); 182 | ASSERT_EQ_FMT(value,state.rcvInt16,"%d"); 183 | 184 | PASS(); 185 | } 186 | 187 | TEST publish_int16_neg() 188 | { 189 | TM_state state; 190 | uint16_t i; 191 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 192 | { 193 | endBuffer[i] = 0; 194 | state.rcvTopic[i] = 0; 195 | } 196 | sizeWritten = 0; 197 | sizeRead = 0; 198 | state.rcvInt16 = 0; 199 | state.called = 0; 200 | 201 | TM_transport transport; 202 | transport.read = read_int; 203 | transport.write = write_int; 204 | transport.readable = readable_int; 205 | transport.writeable = writeable_int; 206 | 207 | char topic[] = "topic"; 208 | int16_t value = -32767; 209 | 210 | init_telemetry(&transport); 211 | 212 | subscribe(callback_int,&state); 213 | 214 | publish_i16(topic, value); 215 | 216 | update_telemetry(); 217 | 218 | ASSERT_EQ(state.called, 1); 219 | ASSERT_STR_EQ(topic,state.rcvTopic); 220 | ASSERT_EQ_FMT(value,state.rcvInt16,"%d"); 221 | 222 | PASS(); 223 | } 224 | 225 | TEST publish_int32() 226 | { 227 | TM_state state; 228 | uint16_t i; 229 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 230 | { 231 | endBuffer[i] = 0; 232 | state.rcvTopic[i] = 0; 233 | } 234 | sizeWritten = 0; 235 | sizeRead = 0; 236 | state.rcvInt32 = 0; 237 | state.called = 0; 238 | 239 | TM_transport transport; 240 | transport.read = read_int; 241 | transport.write = write_int; 242 | transport.readable = readable_int; 243 | transport.writeable = writeable_int; 244 | 245 | char topic[] = " topic "; 246 | int32_t value = 2147483647; 247 | 248 | init_telemetry(&transport); 249 | 250 | subscribe(callback_int,&state); 251 | 252 | publish_i32(topic, value); 253 | 254 | update_telemetry(); 255 | 256 | ASSERT_EQ(state.called, 1); 257 | ASSERT_STR_EQ(topic,state.rcvTopic); 258 | ASSERT_EQ_FMT(value,state.rcvInt32,"%d"); 259 | 260 | PASS(); 261 | } 262 | 263 | TEST publish_int32_neg() 264 | { 265 | TM_state state; 266 | uint16_t i; 267 | for(i = 0 ; i < OUTGOING_BUFFER_SIZE ; i++) 268 | { 269 | endBuffer[i] = 0; 270 | state.rcvTopic[i] = 0; 271 | } 272 | sizeWritten = 0; 273 | sizeRead = 0; 274 | state.rcvInt32 = 0; 275 | state.called = 0; 276 | 277 | TM_transport transport; 278 | transport.read = read_int; 279 | transport.write = write_int; 280 | transport.readable = readable_int; 281 | transport.writeable = writeable_int; 282 | 283 | char topic[] = "topic"; 284 | int32_t value = -2147483647; 285 | 286 | init_telemetry(&transport); 287 | 288 | subscribe(callback_int,&state); 289 | 290 | publish_i32(topic, value); 291 | 292 | update_telemetry(); 293 | 294 | ASSERT_EQ(state.called, 1); 295 | ASSERT_STR_EQ(topic,state.rcvTopic); 296 | ASSERT_EQ_FMT(value,state.rcvInt32,"%d"); 297 | 298 | PASS(); 299 | } 300 | 301 | SUITE(pub_sub_int_suite) { 302 | RUN_TEST(publish_int8); 303 | RUN_TEST(publish_int8_neg); 304 | RUN_TEST(publish_int16); 305 | RUN_TEST(publish_int16_neg); 306 | RUN_TEST(publish_int32); 307 | RUN_TEST(publish_int32_neg); 308 | } 309 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. epub3 to make an epub3 31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 32 | echo. text to make text files 33 | echo. man to make manual pages 34 | echo. texinfo to make Texinfo files 35 | echo. gettext to make PO message catalogs 36 | echo. changes to make an overview over all changed/added/deprecated items 37 | echo. xml to make Docutils-native XML files 38 | echo. pseudoxml to make pseudoxml-XML files for display purposes 39 | echo. linkcheck to check all external links for integrity 40 | echo. doctest to run all doctests embedded in the documentation if enabled 41 | echo. coverage to run coverage check of the documentation if enabled 42 | goto end 43 | ) 44 | 45 | if "%1" == "clean" ( 46 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 47 | del /q /s %BUILDDIR%\* 48 | goto end 49 | ) 50 | 51 | 52 | REM Check if sphinx-build is available and fallback to Python version if any 53 | %SPHINXBUILD% 1>NUL 2>NUL 54 | if errorlevel 9009 goto sphinx_python 55 | goto sphinx_ok 56 | 57 | :sphinx_python 58 | 59 | set SPHINXBUILD=python -m sphinx.__init__ 60 | %SPHINXBUILD% 2> nul 61 | if errorlevel 9009 ( 62 | echo. 63 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 64 | echo.installed, then set the SPHINXBUILD environment variable to point 65 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 66 | echo.may add the Sphinx directory to PATH. 67 | echo. 68 | echo.If you don't have Sphinx installed, grab it from 69 | echo.http://sphinx-doc.org/ 70 | exit /b 1 71 | ) 72 | 73 | :sphinx_ok 74 | 75 | 76 | if "%1" == "html" ( 77 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 78 | if errorlevel 1 exit /b 1 79 | echo. 80 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 81 | goto end 82 | ) 83 | 84 | if "%1" == "dirhtml" ( 85 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 86 | if errorlevel 1 exit /b 1 87 | echo. 88 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 89 | goto end 90 | ) 91 | 92 | if "%1" == "singlehtml" ( 93 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 97 | goto end 98 | ) 99 | 100 | if "%1" == "pickle" ( 101 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 102 | if errorlevel 1 exit /b 1 103 | echo. 104 | echo.Build finished; now you can process the pickle files. 105 | goto end 106 | ) 107 | 108 | if "%1" == "json" ( 109 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished; now you can process the JSON files. 113 | goto end 114 | ) 115 | 116 | if "%1" == "htmlhelp" ( 117 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished; now you can run HTML Help Workshop with the ^ 121 | .hhp project file in %BUILDDIR%/htmlhelp. 122 | goto end 123 | ) 124 | 125 | if "%1" == "qthelp" ( 126 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 127 | if errorlevel 1 exit /b 1 128 | echo. 129 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 130 | .qhcp project file in %BUILDDIR%/qthelp, like this: 131 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Telemetry.qhcp 132 | echo.To view the help file: 133 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Telemetry.ghc 134 | goto end 135 | ) 136 | 137 | if "%1" == "devhelp" ( 138 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 139 | if errorlevel 1 exit /b 1 140 | echo. 141 | echo.Build finished. 142 | goto end 143 | ) 144 | 145 | if "%1" == "epub" ( 146 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 147 | if errorlevel 1 exit /b 1 148 | echo. 149 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 150 | goto end 151 | ) 152 | 153 | if "%1" == "epub3" ( 154 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 155 | if errorlevel 1 exit /b 1 156 | echo. 157 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. 158 | goto end 159 | ) 160 | 161 | if "%1" == "latex" ( 162 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 166 | goto end 167 | ) 168 | 169 | if "%1" == "latexpdf" ( 170 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 171 | cd %BUILDDIR%/latex 172 | make all-pdf 173 | cd %~dp0 174 | echo. 175 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 176 | goto end 177 | ) 178 | 179 | if "%1" == "latexpdfja" ( 180 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 181 | cd %BUILDDIR%/latex 182 | make all-pdf-ja 183 | cd %~dp0 184 | echo. 185 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 186 | goto end 187 | ) 188 | 189 | if "%1" == "text" ( 190 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 191 | if errorlevel 1 exit /b 1 192 | echo. 193 | echo.Build finished. The text files are in %BUILDDIR%/text. 194 | goto end 195 | ) 196 | 197 | if "%1" == "man" ( 198 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 199 | if errorlevel 1 exit /b 1 200 | echo. 201 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 202 | goto end 203 | ) 204 | 205 | if "%1" == "texinfo" ( 206 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 207 | if errorlevel 1 exit /b 1 208 | echo. 209 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 210 | goto end 211 | ) 212 | 213 | if "%1" == "gettext" ( 214 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 215 | if errorlevel 1 exit /b 1 216 | echo. 217 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 218 | goto end 219 | ) 220 | 221 | if "%1" == "changes" ( 222 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 223 | if errorlevel 1 exit /b 1 224 | echo. 225 | echo.The overview file is in %BUILDDIR%/changes. 226 | goto end 227 | ) 228 | 229 | if "%1" == "linkcheck" ( 230 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Link check complete; look for any errors in the above output ^ 234 | or in %BUILDDIR%/linkcheck/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "doctest" ( 239 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of doctests in the sources finished, look at the ^ 243 | results in %BUILDDIR%/doctest/output.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "coverage" ( 248 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Testing of coverage in the sources finished, look at the ^ 252 | results in %BUILDDIR%/coverage/python.txt. 253 | goto end 254 | ) 255 | 256 | if "%1" == "xml" ( 257 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 258 | if errorlevel 1 exit /b 1 259 | echo. 260 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 261 | goto end 262 | ) 263 | 264 | if "%1" == "pseudoxml" ( 265 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 266 | if errorlevel 1 exit /b 1 267 | echo. 268 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 269 | goto end 270 | ) 271 | 272 | :end 273 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " epub3 to make an epub3" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | .PHONY: clean 52 | clean: 53 | rm -rf $(BUILDDIR)/* 54 | 55 | .PHONY: html 56 | html: 57 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 58 | @echo 59 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 60 | 61 | .PHONY: dirhtml 62 | dirhtml: 63 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 64 | @echo 65 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 66 | 67 | .PHONY: singlehtml 68 | singlehtml: 69 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 70 | @echo 71 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 72 | 73 | .PHONY: pickle 74 | pickle: 75 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 76 | @echo 77 | @echo "Build finished; now you can process the pickle files." 78 | 79 | .PHONY: json 80 | json: 81 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 82 | @echo 83 | @echo "Build finished; now you can process the JSON files." 84 | 85 | .PHONY: htmlhelp 86 | htmlhelp: 87 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 88 | @echo 89 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 90 | ".hhp project file in $(BUILDDIR)/htmlhelp." 91 | 92 | .PHONY: qthelp 93 | qthelp: 94 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 95 | @echo 96 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 97 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 98 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Telemetry.qhcp" 99 | @echo "To view the help file:" 100 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Telemetry.qhc" 101 | 102 | .PHONY: applehelp 103 | applehelp: 104 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 105 | @echo 106 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 107 | @echo "N.B. You won't be able to view it unless you put it in" \ 108 | "~/Library/Documentation/Help or install it in your application" \ 109 | "bundle." 110 | 111 | .PHONY: devhelp 112 | devhelp: 113 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 114 | @echo 115 | @echo "Build finished." 116 | @echo "To view the help file:" 117 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Telemetry" 118 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Telemetry" 119 | @echo "# devhelp" 120 | 121 | .PHONY: epub 122 | epub: 123 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 124 | @echo 125 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 126 | 127 | .PHONY: epub3 128 | epub3: 129 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 130 | @echo 131 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 132 | 133 | .PHONY: latex 134 | latex: 135 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 136 | @echo 137 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 138 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 139 | "(use \`make latexpdf' here to do that automatically)." 140 | 141 | .PHONY: latexpdf 142 | latexpdf: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through pdflatex..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: latexpdfja 149 | latexpdfja: 150 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 151 | @echo "Running LaTeX files through platex and dvipdfmx..." 152 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 153 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 154 | 155 | .PHONY: text 156 | text: 157 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 158 | @echo 159 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 160 | 161 | .PHONY: man 162 | man: 163 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 164 | @echo 165 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 166 | 167 | .PHONY: texinfo 168 | texinfo: 169 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 170 | @echo 171 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 172 | @echo "Run \`make' in that directory to run these through makeinfo" \ 173 | "(use \`make info' here to do that automatically)." 174 | 175 | .PHONY: info 176 | info: 177 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 178 | @echo "Running Texinfo files through makeinfo..." 179 | make -C $(BUILDDIR)/texinfo info 180 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 181 | 182 | .PHONY: gettext 183 | gettext: 184 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 185 | @echo 186 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 187 | 188 | .PHONY: changes 189 | changes: 190 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 191 | @echo 192 | @echo "The overview file is in $(BUILDDIR)/changes." 193 | 194 | .PHONY: linkcheck 195 | linkcheck: 196 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 197 | @echo 198 | @echo "Link check complete; look for any errors in the above output " \ 199 | "or in $(BUILDDIR)/linkcheck/output.txt." 200 | 201 | .PHONY: doctest 202 | doctest: 203 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 204 | @echo "Testing of doctests in the sources finished, look at the " \ 205 | "results in $(BUILDDIR)/doctest/output.txt." 206 | 207 | .PHONY: coverage 208 | coverage: 209 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 210 | @echo "Testing of coverage in the sources finished, look at the " \ 211 | "results in $(BUILDDIR)/coverage/python.txt." 212 | 213 | .PHONY: xml 214 | xml: 215 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 216 | @echo 217 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 218 | 219 | .PHONY: pseudoxml 220 | pseudoxml: 221 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 222 | @echo 223 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 224 | -------------------------------------------------------------------------------- /src/telemetry/c/telemetry_core.c: -------------------------------------------------------------------------------- 1 | #include "telemetry_core.h" 2 | #include "framing.h" 3 | #include "crc16.h" 4 | #include "dictionnary.h" 5 | 6 | static TM_state * statePtr; 7 | static TM_transport * transportPtr; 8 | static uint8_t incomingBuffer[INCOMING_BUFFER_SIZE]; 9 | static uint8_t outgoingBuffer[OUTGOING_BUFFER_SIZE]; 10 | static char topicBuffer[TOPIC_BUFFER_SIZE]; 11 | 12 | struct nlist * hashtab[HASHSIZE]; 13 | 14 | 15 | static void (*userCallback)(TM_state * s, TM_msg * m); 16 | 17 | uint16_t header(TM_type type); 18 | uint16_t topic(const char * topic, uint16_t crc); 19 | uint16_t payload(const void * payload, uint32_t size, uint16_t crc); 20 | void frame(const char * t, TM_type type, const void * data, uint32_t datasize); 21 | void send(void * buf, uint32_t size); 22 | void on_incoming_frame(uint8_t * storage, uint32_t size); 23 | void on_incoming_error(int32_t errCode); 24 | void emptyCallback(TM_state * s, TM_msg * m); 25 | 26 | void init_telemetry(TM_transport * t) 27 | { 28 | statePtr = NULL; 29 | transportPtr = t; 30 | userCallback = emptyCallback; 31 | 32 | // Setup framing 33 | initialize_framing(); 34 | incoming_storage(incomingBuffer,INCOMING_BUFFER_SIZE); 35 | outgoing_storage(outgoingBuffer, OUTGOING_BUFFER_SIZE); 36 | set_on_incoming_frame(on_incoming_frame); 37 | set_on_incoming_error(on_incoming_error); 38 | 39 | // Setup update dictionnary 40 | init_table(hashtab); 41 | } 42 | 43 | void attach(const char * name, void (*callback)(TM_msg * m)) 44 | { 45 | install(hashtab, name, (void*)(callback), ptr_function); 46 | } 47 | 48 | void attach_f32(const char * name, float * variable) 49 | { 50 | install(hashtab, name, (void*)(variable), ptr_f32); 51 | } 52 | 53 | void attach_u8(const char * name, uint8_t * variable) 54 | { 55 | install(hashtab, name, (void*)(variable), ptr_u8); 56 | } 57 | 58 | void attach_u16(const char * name, uint16_t * variable) 59 | { 60 | install(hashtab, name, (void*)(variable), ptr_u16); 61 | } 62 | 63 | void attach_u32(const char * name, uint32_t * variable) 64 | { 65 | install(hashtab, name, (void*)(variable), ptr_u32); 66 | } 67 | 68 | void attach_i8(const char * name, int8_t * variable) 69 | { 70 | install(hashtab, name, (void*)(variable), ptr_i8); 71 | } 72 | 73 | void attach_i16(const char * name, int16_t * variable) 74 | { 75 | install(hashtab, name, (void*)(variable), ptr_i16); 76 | } 77 | 78 | void attach_i32(const char * name, int32_t * variable) 79 | { 80 | install(hashtab, name, (void*)(variable), ptr_i32); 81 | } 82 | 83 | void publish(const char * t, const char * msg) 84 | { 85 | frame(t,TM_string,msg,strlen(msg)); 86 | } 87 | 88 | void publish_u8(const char * t, uint8_t msg) 89 | { 90 | void * ptr = (void *)(&msg); 91 | frame(t,TM_uint8,ptr,1); 92 | } 93 | 94 | void publish_u16(const char * t, uint16_t msg) 95 | { 96 | void * ptr = (void *)(&msg); 97 | frame(t,TM_uint16,ptr,2); 98 | } 99 | 100 | void publish_u32(const char * t, uint32_t msg) 101 | { 102 | void * ptr = (void *)(&msg); 103 | frame(t,TM_uint32,ptr,4); 104 | } 105 | 106 | void publish_i8(const char * t, int8_t msg) 107 | { 108 | void * ptr = (void *)(&msg); 109 | frame(t,TM_int8,ptr,1); 110 | } 111 | 112 | void publish_i16(const char * t, int16_t msg) 113 | { 114 | void * ptr = (void *)(&msg); 115 | frame(t,TM_int16,ptr,2); 116 | } 117 | 118 | void publish_i32(const char * t, int32_t msg) 119 | { 120 | void * ptr = (void *)(&msg); 121 | frame(t,TM_int32,ptr,4); 122 | } 123 | 124 | void publish_f32(const char * t, float msg) 125 | { 126 | void * ptr = (void *)(&msg); 127 | frame(t,TM_float32,ptr,4); 128 | } 129 | 130 | void subscribe(void (*callback)(TM_state* s, TM_msg* m), TM_state * s) 131 | { 132 | statePtr = s; 133 | userCallback = callback; 134 | } 135 | 136 | void update_telemetry() 137 | { 138 | // If user forgot to define transport by calling init_telemetry, abort 139 | if(!transportPtr) 140 | return; 141 | 142 | uint32_t amount = transportPtr->readable(); 143 | uint32_t i = 0 ; 144 | for(i = 0 ; i < amount ; i++) 145 | { 146 | uint8_t c; 147 | transportPtr->read(&c,1); 148 | feed(c); 149 | } 150 | } 151 | 152 | uint16_t header(TM_type type) 153 | { 154 | // header data 155 | uint16_t h = type; 156 | uint8_t * ptr = (uint8_t*)(&h); 157 | 158 | // add data to frame 159 | append2(h); 160 | 161 | // compute crc and return it 162 | return crc16(ptr, 2); 163 | } 164 | 165 | uint16_t topic(const char * t, uint16_t crc) 166 | { 167 | const uint8_t * ptr = (uint8_t*)t; 168 | uint32_t i = 0 ; 169 | for(i = 0 ; i < strlen(t) ; i++) 170 | { 171 | // TODO : Replace with Huffman compression 172 | append(ptr[i]); 173 | crc = crc16_recursive(ptr[i], crc); 174 | } 175 | // Add NULL character 176 | append(0); 177 | return crc16_recursive(0,crc); 178 | } 179 | 180 | uint16_t payload(const void * p, uint32_t size, uint16_t crc) 181 | { 182 | const uint8_t * ptr = (uint8_t*)p; 183 | uint32_t i = 0 ; 184 | for(i = 0 ; i < size ; i++) 185 | { 186 | append(ptr[i]); 187 | crc = crc16_recursive(ptr[i], crc); 188 | } 189 | return crc; 190 | } 191 | 192 | void frame(const char * t, TM_type type, const void * data, uint32_t datasize) 193 | { 194 | // start new frame 195 | begin_frame(); 196 | 197 | // header 198 | uint16_t crc = header(type); 199 | 200 | // topic 201 | crc = topic(t, crc); 202 | 203 | // payload 204 | crc = payload(data, datasize, crc); 205 | 206 | // crc 207 | append2(crc); 208 | 209 | // complete frame 210 | uint32_t bytesAmount = end_frame(); 211 | 212 | // send data 213 | send(outgoingBuffer, bytesAmount); 214 | } 215 | 216 | void send(void * buf, uint32_t size) 217 | { 218 | // If user forgot to define transport by calling init_telemetry, abort 219 | if(!transportPtr) 220 | return; 221 | 222 | if(transportPtr->writeable() && size > 0) 223 | { 224 | transportPtr->write(outgoingBuffer, size); 225 | } 226 | } 227 | 228 | void try_update_hashtable(TM_msg * msg) 229 | { 230 | struct nlist * np = lookup(hashtab, msg->topic); 231 | 232 | // Topic not found 233 | if(np == NULL) 234 | return; 235 | 236 | switch(msg->type) 237 | { 238 | case TM_float32: 239 | // If hashtable has an entry of type float 32 under received topic 240 | if (np->ptr_f32 == NULL) 241 | break; 242 | emplace_f32(msg, np->ptr_f32); 243 | break; 244 | case TM_uint8: 245 | // If hashtable has an entry of type float 32 under received topic 246 | if (np->ptr_u8 == NULL) 247 | break; 248 | emplace_u8(msg, np->ptr_u8); 249 | break; 250 | case TM_uint16: 251 | // If hashtable has an entry of type float 32 under received topic 252 | if (np->ptr_u16 == NULL) 253 | break; 254 | emplace_u16(msg, np->ptr_u16); 255 | break; 256 | case TM_uint32: 257 | // If hashtable has an entry of type float 32 under received topic 258 | if (np->ptr_u32 == NULL) 259 | break; 260 | emplace_u32(msg, np->ptr_u32); 261 | break; 262 | case TM_int8: 263 | // If hashtable has an entry of type float 32 under received topic 264 | if (np->ptr_i8 == NULL) 265 | break; 266 | emplace_i8(msg, np->ptr_i8); 267 | break; 268 | case TM_int16: 269 | // If hashtable has an entry of type float 32 under received topic 270 | if (np->ptr_i16 == NULL) 271 | break; 272 | emplace_i16(msg, np->ptr_i16); 273 | break; 274 | case TM_int32: 275 | // If hashtable has an entry of type float 32 under received topic 276 | if (np->ptr_i32 == NULL) 277 | break; 278 | emplace_i32(msg, np->ptr_i32); 279 | break; 280 | } 281 | } 282 | 283 | void on_incoming_frame(uint8_t * storage, uint32_t size) 284 | { 285 | if(size < 2) 286 | return; 287 | // Read header 288 | uint16_t head; 289 | uint8_t * ptr; 290 | ptr = (uint8_t*)(&head); 291 | memcpy(ptr,storage,2); 292 | 293 | // Read topic 294 | uint32_t cursor = 2; 295 | uint32_t topicSize = 0; 296 | while(cursor < size) 297 | { 298 | if(storage[cursor] == 0) 299 | break; 300 | topicSize++; 301 | cursor++; 302 | } 303 | 304 | if(topicSize == 0) 305 | return; 306 | 307 | // payload = total - header - topic - /0 - crc 308 | int32_t payloadSize = size - 2 - topicSize - 1 - 2; 309 | 310 | if(payloadSize <= 0) 311 | return; 312 | 313 | // Check crc 314 | uint16_t expected_crc = crc16(storage, size-2); 315 | uint16_t rcv_crc; 316 | ptr = (uint8_t*)(&rcv_crc); 317 | memcpy(ptr,storage+size-2,2); 318 | 319 | if(expected_crc != rcv_crc) 320 | return; 321 | 322 | // Store topic 323 | char * t = (char*)(storage); 324 | strcpy(topicBuffer, t + 2); 325 | 326 | // ptr to beginning of payload 327 | ptr = (uint8_t*)(storage) + (uint32_t)(2 + topicSize + 1); 328 | 329 | TM_msg packet; 330 | packet.topic = topicBuffer; 331 | packet.type = (TM_type)head; 332 | packet.buffer = (void *)(ptr); 333 | packet.size = (uint32_t)payloadSize; 334 | 335 | // Try update variable if found in hash table 336 | try_update_hashtable(&packet); 337 | 338 | // Call global handler 339 | userCallback(statePtr,&packet); 340 | } 341 | 342 | void on_incoming_error(int32_t errCode) 343 | { 344 | // TODO : Error management 345 | } 346 | 347 | void emptyCallback(TM_state * s, TM_msg * m) 348 | { 349 | // Called only if the user forgot to subscribe a callback 350 | } 351 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Telemetry documentation build configuration file, created by 5 | # sphinx-quickstart on Thu Apr 14 11:36:29 2016. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import sys 17 | import os 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix(es) of source filenames. 38 | # You can specify multiple suffix as a list of string: 39 | # source_suffix = ['.rst', '.md'] 40 | source_suffix = '.rst' 41 | 42 | # The encoding of source files. 43 | #source_encoding = 'utf-8-sig' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = 'Telemetry' 50 | copyright = '2016, Remi Beges' 51 | author = 'Remi Beges' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '2.0.0' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '2.0.0' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # There are two options for replacing |today|: either, you set today to some 70 | # non-false value, then it is used: 71 | #today = '' 72 | # Else, today_fmt is used as the format for a strftime call. 73 | #today_fmt = '%B %d, %Y' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | # This patterns also effect to html_static_path and html_extra_path 78 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 79 | 80 | # The reST default role (used for this markup: `text`) to use for all 81 | # documents. 82 | #default_role = None 83 | 84 | # If true, '()' will be appended to :func: etc. cross-reference text. 85 | #add_function_parentheses = True 86 | 87 | # If true, the current module name will be prepended to all description 88 | # unit titles (such as .. function::). 89 | #add_module_names = True 90 | 91 | # If true, sectionauthor and moduleauthor directives will be shown in the 92 | # output. They are ignored by default. 93 | #show_authors = False 94 | 95 | # The name of the Pygments (syntax highlighting) style to use. 96 | pygments_style = 'sphinx' 97 | 98 | # A list of ignored prefixes for module index sorting. 99 | #modindex_common_prefix = [] 100 | 101 | # If true, keep warnings as "system message" paragraphs in the built documents. 102 | #keep_warnings = False 103 | 104 | # If true, `todo` and `todoList` produce output, else they produce nothing. 105 | todo_include_todos = False 106 | 107 | 108 | # -- Options for HTML output ---------------------------------------------- 109 | 110 | # The theme to use for HTML and HTML Help pages. See the documentation for 111 | # a list of builtin themes. 112 | html_theme = 'alabaster' 113 | 114 | # Theme options are theme-specific and customize the look and feel of a theme 115 | # further. For a list of options available for each theme, see the 116 | # documentation. 117 | #html_theme_options = {} 118 | 119 | # Add any paths that contain custom themes here, relative to this directory. 120 | #html_theme_path = [] 121 | 122 | # The name for this set of Sphinx documents. 123 | # " v documentation" by default. 124 | #html_title = 'Telemetry v2.0.0' 125 | 126 | # A shorter title for the navigation bar. Default is the same as html_title. 127 | #html_short_title = None 128 | 129 | # The name of an image file (relative to this directory) to place at the top 130 | # of the sidebar. 131 | #html_logo = None 132 | 133 | # The name of an image file (relative to this directory) to use as a favicon of 134 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 135 | # pixels large. 136 | #html_favicon = None 137 | 138 | # Add any paths that contain custom static files (such as style sheets) here, 139 | # relative to this directory. They are copied after the builtin static files, 140 | # so a file named "default.css" will overwrite the builtin "default.css". 141 | html_static_path = ['_static'] 142 | 143 | # Add any extra paths that contain custom files (such as robots.txt or 144 | # .htaccess) here, relative to this directory. These files are copied 145 | # directly to the root of the documentation. 146 | #html_extra_path = [] 147 | 148 | # If not None, a 'Last updated on:' timestamp is inserted at every page 149 | # bottom, using the given strftime format. 150 | # The empty string is equivalent to '%b %d, %Y'. 151 | #html_last_updated_fmt = None 152 | 153 | # If true, SmartyPants will be used to convert quotes and dashes to 154 | # typographically correct entities. 155 | #html_use_smartypants = True 156 | 157 | # Custom sidebar templates, maps document names to template names. 158 | #html_sidebars = {} 159 | 160 | # Additional templates that should be rendered to pages, maps page names to 161 | # template names. 162 | #html_additional_pages = {} 163 | 164 | # If false, no module index is generated. 165 | #html_domain_indices = True 166 | 167 | # If false, no index is generated. 168 | #html_use_index = True 169 | 170 | # If true, the index is split into individual pages for each letter. 171 | #html_split_index = False 172 | 173 | # If true, links to the reST sources are added to the pages. 174 | #html_show_sourcelink = True 175 | 176 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 177 | #html_show_sphinx = True 178 | 179 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 180 | #html_show_copyright = True 181 | 182 | # If true, an OpenSearch description file will be output, and all pages will 183 | # contain a tag referring to it. The value of this option must be the 184 | # base URL from which the finished HTML is served. 185 | #html_use_opensearch = '' 186 | 187 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 188 | #html_file_suffix = None 189 | 190 | # Language to be used for generating the HTML full-text search index. 191 | # Sphinx supports the following languages: 192 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 193 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' 194 | #html_search_language = 'en' 195 | 196 | # A dictionary with options for the search language support, empty by default. 197 | # 'ja' uses this config value. 198 | # 'zh' user can custom change `jieba` dictionary path. 199 | #html_search_options = {'type': 'default'} 200 | 201 | # The name of a javascript file (relative to the configuration directory) that 202 | # implements a search results scorer. If empty, the default will be used. 203 | #html_search_scorer = 'scorer.js' 204 | 205 | # Output file base name for HTML help builder. 206 | htmlhelp_basename = 'Telemetrydoc' 207 | 208 | # -- Options for LaTeX output --------------------------------------------- 209 | 210 | latex_elements = { 211 | # The paper size ('letterpaper' or 'a4paper'). 212 | #'papersize': 'letterpaper', 213 | 214 | # The font size ('10pt', '11pt' or '12pt'). 215 | #'pointsize': '10pt', 216 | 217 | # Additional stuff for the LaTeX preamble. 218 | #'preamble': '', 219 | 220 | # Latex figure (float) alignment 221 | #'figure_align': 'htbp', 222 | } 223 | 224 | # Grouping the document tree into LaTeX files. List of tuples 225 | # (source start file, target name, title, 226 | # author, documentclass [howto, manual, or own class]). 227 | latex_documents = [ 228 | (master_doc, 'Telemetry.tex', 'Telemetry Documentation', 229 | 'Remi Beges', 'manual'), 230 | ] 231 | 232 | # The name of an image file (relative to this directory) to place at the top of 233 | # the title page. 234 | #latex_logo = None 235 | 236 | # For "manual" documents, if this is true, then toplevel headings are parts, 237 | # not chapters. 238 | #latex_use_parts = False 239 | 240 | # If true, show page references after internal links. 241 | #latex_show_pagerefs = False 242 | 243 | # If true, show URL addresses after external links. 244 | #latex_show_urls = False 245 | 246 | # Documents to append as an appendix to all manuals. 247 | #latex_appendices = [] 248 | 249 | # If false, no module index is generated. 250 | #latex_domain_indices = True 251 | 252 | 253 | # -- Options for manual page output --------------------------------------- 254 | 255 | # One entry per manual page. List of tuples 256 | # (source start file, name, description, authors, manual section). 257 | man_pages = [ 258 | (master_doc, 'telemetry', 'Telemetry Documentation', 259 | [author], 1) 260 | ] 261 | 262 | # If true, show URL addresses after external links. 263 | #man_show_urls = False 264 | 265 | 266 | # -- Options for Texinfo output ------------------------------------------- 267 | 268 | # Grouping the document tree into Texinfo files. List of tuples 269 | # (source start file, target name, title, author, 270 | # dir menu entry, description, category) 271 | texinfo_documents = [ 272 | (master_doc, 'Telemetry', 'Telemetry Documentation', 273 | author, 'Telemetry', 'One line description of project.', 274 | 'Miscellaneous'), 275 | ] 276 | 277 | # Documents to append as an appendix to all manuals. 278 | #texinfo_appendices = [] 279 | 280 | # If false, no module index is generated. 281 | #texinfo_domain_indices = True 282 | 283 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 284 | #texinfo_show_urls = 'footnote' 285 | 286 | # If true, do not generate a @detailmenu in the "Top" node's menu. 287 | #texinfo_no_detailmenu = False 288 | -------------------------------------------------------------------------------- /test/headers/greatest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 Scott Vokes 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef GREATEST_H 18 | #define GREATEST_H 19 | 20 | /* 1.1.0 */ 21 | #define GREATEST_VERSION_MAJOR 1 22 | #define GREATEST_VERSION_MINOR 1 23 | #define GREATEST_VERSION_PATCH 0 24 | 25 | /* A unit testing system for C, contained in 1 file. 26 | * It doesn't use dynamic allocation or depend on anything 27 | * beyond ANSI C89. 28 | * 29 | * An up-to-date version can be found at: 30 | * https://github.com/silentbicycle/greatest/ 31 | */ 32 | 33 | 34 | /********************************************************************* 35 | * Minimal test runner template 36 | *********************************************************************/ 37 | #if 0 38 | 39 | #include "greatest.h" 40 | 41 | TEST foo_should_foo(void) { 42 | PASS(); 43 | } 44 | 45 | static void setup_cb(void *data) { 46 | printf("setup callback for each test case\n"); 47 | } 48 | 49 | static void teardown_cb(void *data) { 50 | printf("teardown callback for each test case\n"); 51 | } 52 | 53 | SUITE(suite) { 54 | /* Optional setup/teardown callbacks which will be run before/after 55 | * every test case. If using a test suite, they will be cleared when 56 | * the suite finishes. */ 57 | SET_SETUP(setup_cb, voidp_to_callback_data); 58 | SET_TEARDOWN(teardown_cb, voidp_to_callback_data); 59 | 60 | RUN_TEST(foo_should_foo); 61 | } 62 | 63 | /* Add definitions that need to be in the test runner's main file. */ 64 | GREATEST_MAIN_DEFS(); 65 | 66 | /* Set up, run suite(s) of tests, report pass/fail/skip stats. */ 67 | int run_tests(void) { 68 | GREATEST_INIT(); /* init. greatest internals */ 69 | /* List of suites to run (if any). */ 70 | RUN_SUITE(suite); 71 | 72 | /* Tests can also be run directly, without using test suites. */ 73 | RUN_TEST(foo_should_foo); 74 | 75 | GREATEST_PRINT_REPORT(); /* display results */ 76 | return greatest_all_passed(); 77 | } 78 | 79 | /* main(), for a standalone command-line test runner. 80 | * This replaces run_tests above, and adds command line option 81 | * handling and exiting with a pass/fail status. */ 82 | int main(int argc, char **argv) { 83 | GREATEST_MAIN_BEGIN(); /* init & parse command-line args */ 84 | RUN_SUITE(suite); 85 | GREATEST_MAIN_END(); /* display results */ 86 | } 87 | 88 | #endif 89 | /*********************************************************************/ 90 | 91 | 92 | #include 93 | #include 94 | #include 95 | 96 | /*********** 97 | * Options * 98 | ***********/ 99 | 100 | /* Default column width for non-verbose output. */ 101 | #ifndef GREATEST_DEFAULT_WIDTH 102 | #define GREATEST_DEFAULT_WIDTH 72 103 | #endif 104 | 105 | /* FILE *, for test logging. */ 106 | #ifndef GREATEST_STDOUT 107 | #define GREATEST_STDOUT stdout 108 | #endif 109 | 110 | /* Remove GREATEST_ prefix from most commonly used symbols? */ 111 | #ifndef GREATEST_USE_ABBREVS 112 | #define GREATEST_USE_ABBREVS 1 113 | #endif 114 | 115 | /* Set to 0 to disable all use of setjmp/longjmp. */ 116 | #ifndef GREATEST_USE_LONGJMP 117 | #define GREATEST_USE_LONGJMP 1 118 | #endif 119 | 120 | #if GREATEST_USE_LONGJMP 121 | #include 122 | #endif 123 | 124 | /* Set to 0 to disable all use of time.h / clock(). */ 125 | #ifndef GREATEST_USE_TIME 126 | #define GREATEST_USE_TIME 1 127 | #endif 128 | 129 | #if GREATEST_USE_TIME 130 | #include 131 | #endif 132 | 133 | /* Floating point type, for ASSERT_IN_RANGE. */ 134 | #ifndef GREATEST_FLOAT 135 | #define GREATEST_FLOAT double 136 | #define GREATEST_FLOAT_FMT "%g" 137 | #endif 138 | 139 | /********* 140 | * Types * 141 | *********/ 142 | 143 | /* Info for the current running suite. */ 144 | typedef struct greatest_suite_info { 145 | unsigned int tests_run; 146 | unsigned int passed; 147 | unsigned int failed; 148 | unsigned int skipped; 149 | 150 | #if GREATEST_USE_TIME 151 | /* timers, pre/post running suite and individual tests */ 152 | clock_t pre_suite; 153 | clock_t post_suite; 154 | clock_t pre_test; 155 | clock_t post_test; 156 | #endif 157 | } greatest_suite_info; 158 | 159 | /* Type for a suite function. */ 160 | typedef void (greatest_suite_cb)(void); 161 | 162 | /* Types for setup/teardown callbacks. If non-NULL, these will be run 163 | * and passed the pointer to their additional data. */ 164 | typedef void (greatest_setup_cb)(void *udata); 165 | typedef void (greatest_teardown_cb)(void *udata); 166 | 167 | /* Type for an equality comparison between two pointers of the same type. 168 | * Should return non-0 if equal, otherwise 0. 169 | * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 170 | typedef int greatest_equal_cb(const void *exp, const void *got, void *udata); 171 | 172 | /* Type for a callback that prints a value pointed to by T. 173 | * Return value has the same meaning as printf's. 174 | * UDATA is a closure value, passed through from ASSERT_EQUAL_T[m]. */ 175 | typedef int greatest_printf_cb(const void *t, void *udata); 176 | 177 | /* Callbacks for an arbitrary type; needed for type-specific 178 | * comparisons via GREATEST_ASSERT_EQUAL_T[m].*/ 179 | typedef struct greatest_type_info { 180 | greatest_equal_cb *equal; 181 | greatest_printf_cb *print; 182 | } greatest_type_info; 183 | 184 | /* Callbacks for string type. */ 185 | extern greatest_type_info greatest_type_info_string; 186 | 187 | typedef enum { 188 | GREATEST_FLAG_FIRST_FAIL = 0x01, 189 | GREATEST_FLAG_LIST_ONLY = 0x02 190 | } greatest_flag_t; 191 | 192 | /* Struct containing all test runner state. */ 193 | typedef struct greatest_run_info { 194 | unsigned char flags; 195 | unsigned char verbosity; 196 | unsigned int tests_run; /* total test count */ 197 | 198 | /* overall pass/fail/skip counts */ 199 | unsigned int passed; 200 | unsigned int failed; 201 | unsigned int skipped; 202 | unsigned int assertions; 203 | 204 | /* currently running test suite */ 205 | greatest_suite_info suite; 206 | 207 | /* info to print about the most recent failure */ 208 | const char *fail_file; 209 | unsigned int fail_line; 210 | const char *msg; 211 | 212 | /* current setup/teardown hooks and userdata */ 213 | greatest_setup_cb *setup; 214 | void *setup_udata; 215 | greatest_teardown_cb *teardown; 216 | void *teardown_udata; 217 | 218 | /* formatting info for ".....s...F"-style output */ 219 | unsigned int col; 220 | unsigned int width; 221 | 222 | /* only run a specific suite or test */ 223 | const char *suite_filter; 224 | const char *test_filter; 225 | 226 | #if GREATEST_USE_TIME 227 | /* overall timers */ 228 | clock_t begin; 229 | clock_t end; 230 | #endif 231 | 232 | #if GREATEST_USE_LONGJMP 233 | jmp_buf jump_dest; 234 | #endif 235 | } greatest_run_info; 236 | 237 | struct greatest_report_t { 238 | /* overall pass/fail/skip counts */ 239 | unsigned int passed; 240 | unsigned int failed; 241 | unsigned int skipped; 242 | unsigned int assertions; 243 | }; 244 | 245 | /* Global var for the current testing context. 246 | * Initialized by GREATEST_MAIN_DEFS(). */ 247 | extern greatest_run_info greatest_info; 248 | 249 | 250 | /********************** 251 | * Exported functions * 252 | **********************/ 253 | 254 | /* These are used internally by greatest. */ 255 | void greatest_do_pass(const char *name); 256 | void greatest_do_fail(const char *name); 257 | void greatest_do_skip(const char *name); 258 | int greatest_pre_test(const char *name); 259 | void greatest_post_test(const char *name, int res); 260 | void greatest_usage(const char *name); 261 | int greatest_do_assert_equal_t(const void *exp, const void *got, 262 | greatest_type_info *type_info, void *udata); 263 | 264 | /* These are part of the public greatest API. */ 265 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); 266 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); 267 | int greatest_all_passed(void); 268 | void greatest_set_test_filter(const char *name); 269 | void greatest_set_suite_filter(const char *name); 270 | void greatest_get_report(struct greatest_report_t *report); 271 | unsigned int greatest_get_verbosity(void); 272 | void greatest_set_verbosity(unsigned int verbosity); 273 | void greatest_set_flag(greatest_flag_t flag); 274 | 275 | 276 | /******************** 277 | * Language Support * 278 | ********************/ 279 | 280 | /* If __VA_ARGS__ (C99) is supported, allow parametric testing 281 | * without needing to manually manage the argument struct. */ 282 | #if __STDC_VERSION__ >= 19901L || _MSC_VER >= 1800 283 | #define GREATEST_VA_ARGS 284 | #endif 285 | 286 | 287 | /********** 288 | * Macros * 289 | **********/ 290 | 291 | /* Define a suite. */ 292 | #define GREATEST_SUITE(NAME) void NAME(void); void NAME(void) 293 | 294 | /* Declare a suite, provided by another compilation unit. */ 295 | #define GREATEST_SUITE_EXTERN(NAME) void NAME(void) 296 | 297 | /* Start defining a test function. 298 | * The arguments are not included, to allow parametric testing. */ 299 | #define GREATEST_TEST static greatest_test_res 300 | 301 | /* PASS/FAIL/SKIP result from a test. Used internally. */ 302 | typedef enum { 303 | GREATEST_TEST_RES_PASS = 0, 304 | GREATEST_TEST_RES_FAIL = -1, 305 | GREATEST_TEST_RES_SKIP = 1 306 | } greatest_test_res; 307 | 308 | /* Run a suite. */ 309 | #define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) 310 | 311 | /* Run a test in the current suite. */ 312 | #define GREATEST_RUN_TEST(TEST) \ 313 | do { \ 314 | if (greatest_pre_test(#TEST) == 1) { \ 315 | greatest_test_res res = GREATEST_SAVE_CONTEXT(); \ 316 | if (res == GREATEST_TEST_RES_PASS) { \ 317 | res = TEST(); \ 318 | } \ 319 | greatest_post_test(#TEST, res); \ 320 | } else if (GREATEST_LIST_ONLY()) { \ 321 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 322 | } \ 323 | } while (0) 324 | 325 | /* Run a test in the current suite with one void * argument, 326 | * which can be a pointer to a struct with multiple arguments. */ 327 | #define GREATEST_RUN_TEST1(TEST, ENV) \ 328 | do { \ 329 | if (greatest_pre_test(#TEST) == 1) { \ 330 | int res = TEST(ENV); \ 331 | greatest_post_test(#TEST, res); \ 332 | } else if (GREATEST_LIST_ONLY()) { \ 333 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 334 | } \ 335 | } while (0) 336 | 337 | #ifdef GREATEST_VA_ARGS 338 | #define GREATEST_RUN_TESTp(TEST, ...) \ 339 | do { \ 340 | if (greatest_pre_test(#TEST) == 1) { \ 341 | int res = TEST(__VA_ARGS__); \ 342 | greatest_post_test(#TEST, res); \ 343 | } else if (GREATEST_LIST_ONLY()) { \ 344 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 345 | } \ 346 | } while (0) 347 | #endif 348 | 349 | 350 | /* Check if the test runner is in verbose mode. */ 351 | #define GREATEST_IS_VERBOSE() ((greatest_info.verbosity) > 0) 352 | #define GREATEST_LIST_ONLY() \ 353 | (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) 354 | #define GREATEST_FIRST_FAIL() \ 355 | (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) 356 | #define GREATEST_FAILURE_ABORT() \ 357 | (greatest_info.suite.failed > 0 && GREATEST_FIRST_FAIL()) 358 | 359 | /* Message-less forms of tests defined below. */ 360 | #define GREATEST_PASS() GREATEST_PASSm(NULL) 361 | #define GREATEST_FAIL() GREATEST_FAILm(NULL) 362 | #define GREATEST_SKIP() GREATEST_SKIPm(NULL) 363 | #define GREATEST_ASSERT(COND) \ 364 | GREATEST_ASSERTm(#COND, COND) 365 | #define GREATEST_ASSERT_OR_LONGJMP(COND) \ 366 | GREATEST_ASSERT_OR_LONGJMPm(#COND, COND) 367 | #define GREATEST_ASSERT_FALSE(COND) \ 368 | GREATEST_ASSERT_FALSEm(#COND, COND) 369 | #define GREATEST_ASSERT_EQ(EXP, GOT) \ 370 | GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) 371 | #define GREATEST_ASSERT_EQ_FMT(EXP, GOT, FMT) \ 372 | GREATEST_ASSERT_EQ_FMTm(#EXP " != " #GOT, EXP, GOT, FMT) 373 | #define GREATEST_ASSERT_IN_RANGE(EXP, GOT, TOL) \ 374 | GREATEST_ASSERT_IN_RANGEm(#EXP " != " #GOT " +/- " #TOL, EXP, GOT, TOL) 375 | #define GREATEST_ASSERT_EQUAL_T(EXP, GOT, TYPE_INFO, UDATA) \ 376 | GREATEST_ASSERT_EQUAL_Tm(#EXP " != " #GOT, EXP, GOT, TYPE_INFO, UDATA) 377 | #define GREATEST_ASSERT_STR_EQ(EXP, GOT) \ 378 | GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) 379 | 380 | /* The following forms take an additional message argument first, 381 | * to be displayed by the test runner. */ 382 | 383 | /* Fail if a condition is not true, with message. */ 384 | #define GREATEST_ASSERTm(MSG, COND) \ 385 | do { \ 386 | greatest_info.assertions++; \ 387 | if (!(COND)) { GREATEST_FAILm(MSG); } \ 388 | } while (0) 389 | 390 | /* Fail if a condition is not true, longjmping out of test. */ 391 | #define GREATEST_ASSERT_OR_LONGJMPm(MSG, COND) \ 392 | do { \ 393 | greatest_info.assertions++; \ 394 | if (!(COND)) { GREATEST_FAIL_WITH_LONGJMPm(MSG); } \ 395 | } while (0) 396 | 397 | /* Fail if a condition is not false, with message. */ 398 | #define GREATEST_ASSERT_FALSEm(MSG, COND) \ 399 | do { \ 400 | greatest_info.assertions++; \ 401 | if ((COND)) { GREATEST_FAILm(MSG); } \ 402 | } while (0) 403 | 404 | /* Fail if EXP != GOT (equality comparison by ==). */ 405 | #define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \ 406 | do { \ 407 | greatest_info.assertions++; \ 408 | if ((EXP) != (GOT)) { GREATEST_FAILm(MSG); } \ 409 | } while (0) 410 | 411 | /* Fail if EXP != GOT (equality comparison by ==). */ 412 | #define GREATEST_ASSERT_EQ_FMTm(MSG, EXP, GOT, FMT) \ 413 | do { \ 414 | const char *fmt = ( FMT ); \ 415 | greatest_info.assertions++; \ 416 | if ((EXP) != (GOT)) { \ 417 | fprintf(GREATEST_STDOUT, "\nExpected: "); \ 418 | fprintf(GREATEST_STDOUT, fmt, EXP); \ 419 | fprintf(GREATEST_STDOUT, "\nGot: "); \ 420 | fprintf(GREATEST_STDOUT, fmt, GOT); \ 421 | fprintf(GREATEST_STDOUT, "\n"); \ 422 | GREATEST_FAILm(MSG); \ 423 | } \ 424 | } while (0) 425 | 426 | /* Fail if GOT not in range of EXP +|- TOL. */ 427 | #define GREATEST_ASSERT_IN_RANGEm(MSG, EXP, GOT, TOL) \ 428 | do { \ 429 | GREATEST_FLOAT exp = (EXP); \ 430 | GREATEST_FLOAT got = (GOT); \ 431 | GREATEST_FLOAT tol = (TOL); \ 432 | greatest_info.assertions++; \ 433 | if ((exp > got && exp - got > tol) || \ 434 | (exp < got && got - exp > tol)) { \ 435 | fprintf(GREATEST_STDOUT, \ 436 | "\nExpected: " GREATEST_FLOAT_FMT \ 437 | " +/- " GREATEST_FLOAT_FMT "\n" \ 438 | "Got: " GREATEST_FLOAT_FMT "\n", \ 439 | exp, tol, got); \ 440 | GREATEST_FAILm(MSG); \ 441 | } \ 442 | } while (0) 443 | 444 | /* Fail if EXP is not equal to GOT, according to strcmp. */ 445 | #define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ 446 | do { \ 447 | GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, \ 448 | &greatest_type_info_string, NULL); \ 449 | } while (0) \ 450 | 451 | /* Fail if EXP is not equal to GOT, according to a comparison 452 | * callback in TYPE_INFO. If they are not equal, optionally use a 453 | * print callback in TYPE_INFO to print them. */ 454 | #define GREATEST_ASSERT_EQUAL_Tm(MSG, EXP, GOT, TYPE_INFO, UDATA) \ 455 | do { \ 456 | greatest_type_info *type_info = (TYPE_INFO); \ 457 | greatest_info.assertions++; \ 458 | if (!greatest_do_assert_equal_t(EXP, GOT, \ 459 | type_info, UDATA)) { \ 460 | if (type_info == NULL || type_info->equal == NULL) { \ 461 | GREATEST_FAILm("type_info->equal callback missing!"); \ 462 | } else { \ 463 | GREATEST_FAILm(MSG); \ 464 | } \ 465 | } \ 466 | } while (0) \ 467 | 468 | /* Pass. */ 469 | #define GREATEST_PASSm(MSG) \ 470 | do { \ 471 | greatest_info.msg = MSG; \ 472 | return GREATEST_TEST_RES_PASS; \ 473 | } while (0) 474 | 475 | /* Fail. */ 476 | #define GREATEST_FAILm(MSG) \ 477 | do { \ 478 | greatest_info.fail_file = __FILE__; \ 479 | greatest_info.fail_line = __LINE__; \ 480 | greatest_info.msg = MSG; \ 481 | return GREATEST_TEST_RES_FAIL; \ 482 | } while (0) 483 | 484 | /* Optional GREATEST_FAILm variant that longjmps. */ 485 | #if GREATEST_USE_LONGJMP 486 | #define GREATEST_FAIL_WITH_LONGJMP() GREATEST_FAIL_WITH_LONGJMPm(NULL) 487 | #define GREATEST_FAIL_WITH_LONGJMPm(MSG) \ 488 | do { \ 489 | greatest_info.fail_file = __FILE__; \ 490 | greatest_info.fail_line = __LINE__; \ 491 | greatest_info.msg = MSG; \ 492 | longjmp(greatest_info.jump_dest, GREATEST_TEST_RES_FAIL); \ 493 | } while (0) 494 | #endif 495 | 496 | /* Skip the current test. */ 497 | #define GREATEST_SKIPm(MSG) \ 498 | do { \ 499 | greatest_info.msg = MSG; \ 500 | return GREATEST_TEST_RES_SKIP; \ 501 | } while (0) 502 | 503 | /* Check the result of a subfunction using ASSERT, etc. */ 504 | #define GREATEST_CHECK_CALL(RES) \ 505 | do { \ 506 | int _check_call_res = RES; \ 507 | if (_check_call_res != GREATEST_TEST_RES_PASS) { \ 508 | return _check_call_res; \ 509 | } \ 510 | } while (0) \ 511 | 512 | #if GREATEST_USE_TIME 513 | #define GREATEST_SET_TIME(NAME) \ 514 | NAME = clock(); \ 515 | if (NAME == (clock_t) -1) { \ 516 | fprintf(GREATEST_STDOUT, \ 517 | "clock error: %s\n", #NAME); \ 518 | exit(EXIT_FAILURE); \ 519 | } 520 | 521 | #define GREATEST_CLOCK_DIFF(C1, C2) \ 522 | fprintf(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ 523 | (long unsigned int) (C2) - (long unsigned int)(C1), \ 524 | (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) 525 | #else 526 | #define GREATEST_SET_TIME(UNUSED) 527 | #define GREATEST_CLOCK_DIFF(UNUSED1, UNUSED2) 528 | #endif 529 | 530 | #if GREATEST_USE_LONGJMP 531 | #define GREATEST_SAVE_CONTEXT() \ 532 | /* setjmp returns 0 (GREATEST_TEST_RES_PASS) on first call */ \ 533 | /* so the test runs, then RES_FAIL from FAIL_WITH_LONGJMP. */ \ 534 | ((greatest_test_res)(setjmp(greatest_info.jump_dest))) 535 | #else 536 | #define GREATEST_SAVE_CONTEXT() \ 537 | /*a no-op, since setjmp/longjmp aren't being used */ \ 538 | GREATEST_TEST_RES_PASS 539 | #endif 540 | 541 | /* Include several function definitions in the main test file. */ 542 | #define GREATEST_MAIN_DEFS() \ 543 | \ 544 | /* Is FILTER a subset of NAME? */ \ 545 | static int greatest_name_match(const char *name, \ 546 | const char *filter) { \ 547 | size_t offset = 0; \ 548 | size_t filter_len = strlen(filter); \ 549 | while (name[offset] != '\0') { \ 550 | if (name[offset] == filter[0]) { \ 551 | if (0 == strncmp(&name[offset], filter, filter_len)) { \ 552 | return 1; \ 553 | } \ 554 | } \ 555 | offset++; \ 556 | } \ 557 | \ 558 | return 0; \ 559 | } \ 560 | \ 561 | int greatest_pre_test(const char *name) { \ 562 | if (!GREATEST_LIST_ONLY() \ 563 | && (!GREATEST_FIRST_FAIL() || greatest_info.suite.failed == 0) \ 564 | && (greatest_info.test_filter == NULL || \ 565 | greatest_name_match(name, greatest_info.test_filter))) { \ 566 | GREATEST_SET_TIME(greatest_info.suite.pre_test); \ 567 | if (greatest_info.setup) { \ 568 | greatest_info.setup(greatest_info.setup_udata); \ 569 | } \ 570 | return 1; /* test should be run */ \ 571 | } else { \ 572 | return 0; /* skipped */ \ 573 | } \ 574 | } \ 575 | \ 576 | void greatest_post_test(const char *name, int res) { \ 577 | GREATEST_SET_TIME(greatest_info.suite.post_test); \ 578 | if (greatest_info.teardown) { \ 579 | void *udata = greatest_info.teardown_udata; \ 580 | greatest_info.teardown(udata); \ 581 | } \ 582 | \ 583 | if (res <= GREATEST_TEST_RES_FAIL) { \ 584 | greatest_do_fail(name); \ 585 | } else if (res >= GREATEST_TEST_RES_SKIP) { \ 586 | greatest_do_skip(name); \ 587 | } else if (res == GREATEST_TEST_RES_PASS) { \ 588 | greatest_do_pass(name); \ 589 | } \ 590 | greatest_info.suite.tests_run++; \ 591 | greatest_info.col++; \ 592 | if (GREATEST_IS_VERBOSE()) { \ 593 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ 594 | greatest_info.suite.post_test); \ 595 | fprintf(GREATEST_STDOUT, "\n"); \ 596 | } else if (greatest_info.col % greatest_info.width == 0) { \ 597 | fprintf(GREATEST_STDOUT, "\n"); \ 598 | greatest_info.col = 0; \ 599 | } \ 600 | if (GREATEST_STDOUT == stdout) fflush(stdout); \ 601 | } \ 602 | \ 603 | static void report_suite(void) { \ 604 | if (greatest_info.suite.tests_run > 0) { \ 605 | fprintf(GREATEST_STDOUT, \ 606 | "\n%u test%s - %u passed, %u failed, %u skipped", \ 607 | greatest_info.suite.tests_run, \ 608 | greatest_info.suite.tests_run == 1 ? "" : "s", \ 609 | greatest_info.suite.passed, \ 610 | greatest_info.suite.failed, \ 611 | greatest_info.suite.skipped); \ 612 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ 613 | greatest_info.suite.post_suite); \ 614 | fprintf(GREATEST_STDOUT, "\n"); \ 615 | } \ 616 | } \ 617 | \ 618 | static void update_counts_and_reset_suite(void) { \ 619 | greatest_info.setup = NULL; \ 620 | greatest_info.setup_udata = NULL; \ 621 | greatest_info.teardown = NULL; \ 622 | greatest_info.teardown_udata = NULL; \ 623 | greatest_info.passed += greatest_info.suite.passed; \ 624 | greatest_info.failed += greatest_info.suite.failed; \ 625 | greatest_info.skipped += greatest_info.suite.skipped; \ 626 | greatest_info.tests_run += greatest_info.suite.tests_run; \ 627 | memset(&greatest_info.suite, 0, sizeof(greatest_info.suite)); \ 628 | greatest_info.col = 0; \ 629 | } \ 630 | \ 631 | static void greatest_run_suite(greatest_suite_cb *suite_cb, \ 632 | const char *suite_name) { \ 633 | if (greatest_info.suite_filter && \ 634 | !greatest_name_match(suite_name, greatest_info.suite_filter)) { \ 635 | return; \ 636 | } \ 637 | if (GREATEST_FIRST_FAIL() && greatest_info.failed > 0) { return; } \ 638 | if (greatest_info.suite.tests_run > 0) { /* tests w/out suite */ \ 639 | update_counts_and_reset_suite(); \ 640 | } \ 641 | fprintf(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ 642 | GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ 643 | suite_cb(); \ 644 | GREATEST_SET_TIME(greatest_info.suite.post_suite); \ 645 | report_suite(); \ 646 | } \ 647 | \ 648 | void greatest_do_pass(const char *name) { \ 649 | if (GREATEST_IS_VERBOSE()) { \ 650 | fprintf(GREATEST_STDOUT, "PASS %s: %s", \ 651 | name, greatest_info.msg ? greatest_info.msg : ""); \ 652 | } else { \ 653 | fprintf(GREATEST_STDOUT, "."); \ 654 | } \ 655 | greatest_info.suite.passed++; \ 656 | } \ 657 | \ 658 | void greatest_do_fail(const char *name) { \ 659 | if (GREATEST_IS_VERBOSE()) { \ 660 | fprintf(GREATEST_STDOUT, \ 661 | "FAIL %s: %s (%s:%u)", \ 662 | name, greatest_info.msg ? greatest_info.msg : "", \ 663 | greatest_info.fail_file, greatest_info.fail_line); \ 664 | } else { \ 665 | fprintf(GREATEST_STDOUT, "F"); \ 666 | greatest_info.col++; \ 667 | /* add linebreak if in line of '.'s */ \ 668 | if (greatest_info.col != 0) { \ 669 | fprintf(GREATEST_STDOUT, "\n"); \ 670 | greatest_info.col = 0; \ 671 | } \ 672 | fprintf(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ 673 | name, \ 674 | greatest_info.msg ? greatest_info.msg : "", \ 675 | greatest_info.fail_file, greatest_info.fail_line); \ 676 | } \ 677 | greatest_info.suite.failed++; \ 678 | } \ 679 | \ 680 | void greatest_do_skip(const char *name) { \ 681 | if (GREATEST_IS_VERBOSE()) { \ 682 | fprintf(GREATEST_STDOUT, "SKIP %s: %s", \ 683 | name, \ 684 | greatest_info.msg ? \ 685 | greatest_info.msg : "" ); \ 686 | } else { \ 687 | fprintf(GREATEST_STDOUT, "s"); \ 688 | } \ 689 | greatest_info.suite.skipped++; \ 690 | } \ 691 | \ 692 | int greatest_do_assert_equal_t(const void *exp, const void *got, \ 693 | greatest_type_info *type_info, void *udata) { \ 694 | int eq = 0; \ 695 | if (type_info == NULL || type_info->equal == NULL) { \ 696 | return 0; \ 697 | } \ 698 | eq = type_info->equal(exp, got, udata); \ 699 | if (!eq) { \ 700 | if (type_info->print != NULL) { \ 701 | fprintf(GREATEST_STDOUT, "\nExpected: "); \ 702 | (void)type_info->print(exp, udata); \ 703 | fprintf(GREATEST_STDOUT, "\nGot: "); \ 704 | (void)type_info->print(got, udata); \ 705 | fprintf(GREATEST_STDOUT, "\n"); \ 706 | } else { \ 707 | fprintf(GREATEST_STDOUT, \ 708 | "GREATEST_ASSERT_EQUAL_T failure at %s:%u\n", \ 709 | greatest_info.fail_file, \ 710 | greatest_info.fail_line); \ 711 | } \ 712 | } \ 713 | return eq; \ 714 | } \ 715 | \ 716 | void greatest_usage(const char *name) { \ 717 | fprintf(GREATEST_STDOUT, \ 718 | "Usage: %s [-hlfv] [-s SUITE] [-t TEST]\n" \ 719 | " -h print this Help\n" \ 720 | " -l List suites and their tests, then exit\n" \ 721 | " -f Stop runner after first failure\n" \ 722 | " -v Verbose output\n" \ 723 | " -s SUITE only run suites containing string SUITE\n" \ 724 | " -t TEST only run tests containing string TEST\n", \ 725 | name); \ 726 | } \ 727 | \ 728 | static void greatest_parse_args(int argc, char **argv) { \ 729 | int i = 0; \ 730 | for (i = 1; i < argc; i++) { \ 731 | if (0 == strncmp("-t", argv[i], 2)) { \ 732 | if (argc <= i + 1) { \ 733 | greatest_usage(argv[0]); \ 734 | exit(EXIT_FAILURE); \ 735 | } \ 736 | greatest_info.test_filter = argv[i+1]; \ 737 | i++; \ 738 | } else if (0 == strncmp("-s", argv[i], 2)) { \ 739 | if (argc <= i + 1) { \ 740 | greatest_usage(argv[0]); \ 741 | exit(EXIT_FAILURE); \ 742 | } \ 743 | greatest_info.suite_filter = argv[i+1]; \ 744 | i++; \ 745 | } else if (0 == strncmp("-f", argv[i], 2)) { \ 746 | greatest_info.flags |= GREATEST_FLAG_FIRST_FAIL; \ 747 | } else if (0 == strncmp("-v", argv[i], 2)) { \ 748 | greatest_info.verbosity++; \ 749 | } else if (0 == strncmp("-l", argv[i], 2)) { \ 750 | greatest_info.flags |= GREATEST_FLAG_LIST_ONLY; \ 751 | } else if (0 == strncmp("-h", argv[i], 2)) { \ 752 | greatest_usage(argv[0]); \ 753 | exit(EXIT_SUCCESS); \ 754 | } else if (0 == strncmp("--", argv[i], 2)) { \ 755 | break; \ 756 | } else { \ 757 | fprintf(GREATEST_STDOUT, \ 758 | "Unknown argument '%s'\n", argv[i]); \ 759 | greatest_usage(argv[0]); \ 760 | exit(EXIT_FAILURE); \ 761 | } \ 762 | } \ 763 | } \ 764 | \ 765 | int greatest_all_passed(void) { return (greatest_info.failed == 0); } \ 766 | \ 767 | void greatest_set_test_filter(const char *name) { \ 768 | greatest_info.test_filter = name; \ 769 | } \ 770 | \ 771 | void greatest_set_suite_filter(const char *name) { \ 772 | greatest_info.suite_filter = name; \ 773 | } \ 774 | \ 775 | void greatest_get_report(struct greatest_report_t *report) { \ 776 | if (report) { \ 777 | report->passed = greatest_info.passed; \ 778 | report->failed = greatest_info.failed; \ 779 | report->skipped = greatest_info.skipped; \ 780 | report->assertions = greatest_info.assertions; \ 781 | } \ 782 | } \ 783 | \ 784 | unsigned int greatest_get_verbosity(void) { \ 785 | return greatest_info.verbosity; \ 786 | } \ 787 | \ 788 | void greatest_set_verbosity(unsigned int verbosity) { \ 789 | greatest_info.verbosity = (unsigned char)verbosity; \ 790 | } \ 791 | \ 792 | void greatest_set_flag(greatest_flag_t flag) { \ 793 | greatest_info.flags |= flag; \ 794 | } \ 795 | \ 796 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ 797 | greatest_info.setup = cb; \ 798 | greatest_info.setup_udata = udata; \ 799 | } \ 800 | \ 801 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \ 802 | void *udata) { \ 803 | greatest_info.teardown = cb; \ 804 | greatest_info.teardown_udata = udata; \ 805 | } \ 806 | \ 807 | static int greatest_string_equal_cb(const void *exp, const void *got, \ 808 | void *udata) { \ 809 | (void)udata; \ 810 | return (0 == strcmp((const char *)exp, (const char *)got)); \ 811 | } \ 812 | \ 813 | static int greatest_string_printf_cb(const void *t, void *udata) { \ 814 | (void)udata; \ 815 | return fprintf(GREATEST_STDOUT, "%s", (const char *)t); \ 816 | } \ 817 | \ 818 | greatest_type_info greatest_type_info_string = { \ 819 | greatest_string_equal_cb, \ 820 | greatest_string_printf_cb, \ 821 | }; \ 822 | \ 823 | greatest_run_info greatest_info 824 | 825 | /* Init internals. */ 826 | #define GREATEST_INIT() \ 827 | do { \ 828 | /* Suppress unused function warning if features aren't used */ \ 829 | (void)greatest_run_suite; \ 830 | (void)greatest_parse_args; \ 831 | \ 832 | memset(&greatest_info, 0, sizeof(greatest_info)); \ 833 | greatest_info.width = GREATEST_DEFAULT_WIDTH; \ 834 | GREATEST_SET_TIME(greatest_info.begin); \ 835 | } while (0) \ 836 | 837 | /* Handle command-line arguments, etc. */ 838 | #define GREATEST_MAIN_BEGIN() \ 839 | do { \ 840 | GREATEST_INIT(); \ 841 | greatest_parse_args(argc, argv); \ 842 | } while (0) 843 | 844 | /* Report passes, failures, skipped tests, the number of 845 | * assertions, and the overall run time. */ 846 | #define GREATEST_PRINT_REPORT() \ 847 | do { \ 848 | if (!GREATEST_LIST_ONLY()) { \ 849 | update_counts_and_reset_suite(); \ 850 | GREATEST_SET_TIME(greatest_info.end); \ 851 | fprintf(GREATEST_STDOUT, \ 852 | "\nTotal: %u test%s", \ 853 | greatest_info.tests_run, \ 854 | greatest_info.tests_run == 1 ? "" : "s"); \ 855 | GREATEST_CLOCK_DIFF(greatest_info.begin, \ 856 | greatest_info.end); \ 857 | fprintf(GREATEST_STDOUT, ", %u assertion%s\n", \ 858 | greatest_info.assertions, \ 859 | greatest_info.assertions == 1 ? "" : "s"); \ 860 | fprintf(GREATEST_STDOUT, \ 861 | "Pass: %u, fail: %u, skip: %u.\n", \ 862 | greatest_info.passed, \ 863 | greatest_info.failed, greatest_info.skipped); \ 864 | } \ 865 | } while (0) 866 | 867 | /* Report results, exit with exit status based on results. */ 868 | #define GREATEST_MAIN_END() \ 869 | do { \ 870 | GREATEST_PRINT_REPORT(); \ 871 | return (greatest_all_passed() ? EXIT_SUCCESS : EXIT_FAILURE); \ 872 | } while (0) 873 | 874 | /* Make abbreviations without the GREATEST_ prefix for the 875 | * most commonly used symbols. */ 876 | #if GREATEST_USE_ABBREVS 877 | #define TEST GREATEST_TEST 878 | #define SUITE GREATEST_SUITE 879 | #define SUITE_EXTERN GREATEST_SUITE_EXTERN 880 | #define RUN_TEST GREATEST_RUN_TEST 881 | #define RUN_TEST1 GREATEST_RUN_TEST1 882 | #define RUN_SUITE GREATEST_RUN_SUITE 883 | #define ASSERT GREATEST_ASSERT 884 | #define ASSERTm GREATEST_ASSERTm 885 | #define ASSERT_FALSE GREATEST_ASSERT_FALSE 886 | #define ASSERT_EQ GREATEST_ASSERT_EQ 887 | #define ASSERT_EQ_FMT GREATEST_ASSERT_EQ_FMT 888 | #define ASSERT_IN_RANGE GREATEST_ASSERT_IN_RANGE 889 | #define ASSERT_EQUAL_T GREATEST_ASSERT_EQUAL_T 890 | #define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ 891 | #define ASSERT_FALSEm GREATEST_ASSERT_FALSEm 892 | #define ASSERT_EQm GREATEST_ASSERT_EQm 893 | #define ASSERT_EQ_FMTm GREATEST_ASSERT_EQ_FMTm 894 | #define ASSERT_IN_RANGEm GREATEST_ASSERT_IN_RANGEm 895 | #define ASSERT_EQUAL_Tm GREATEST_ASSERT_EQUAL_Tm 896 | #define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm 897 | #define PASS GREATEST_PASS 898 | #define FAIL GREATEST_FAIL 899 | #define SKIP GREATEST_SKIP 900 | #define PASSm GREATEST_PASSm 901 | #define FAILm GREATEST_FAILm 902 | #define SKIPm GREATEST_SKIPm 903 | #define SET_SETUP GREATEST_SET_SETUP_CB 904 | #define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB 905 | #define CHECK_CALL GREATEST_CHECK_CALL 906 | 907 | #ifdef GREATEST_VA_ARGS 908 | #define RUN_TESTp GREATEST_RUN_TESTp 909 | #endif 910 | 911 | #if GREATEST_USE_LONGJMP 912 | #define ASSERT_OR_LONGJMP GREATEST_ASSERT_OR_LONGJMP 913 | #define ASSERT_OR_LONGJMPm GREATEST_ASSERT_OR_LONGJMPm 914 | #define FAIL_WITH_LONGJMP GREATEST_FAIL_WITH_LONGJMP 915 | #define FAIL_WITH_LONGJMPm GREATEST_FAIL_WITH_LONGJMPm 916 | #endif 917 | 918 | #endif /* USE_ABBREVS */ 919 | 920 | #endif 921 | --------------------------------------------------------------------------------