From 194e3811b7abec4adb0f4bc9108f75a7e277e29f Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 6 May 2026 18:43:49 +0000 Subject: [PATCH 1/9] cmakelists add hw5 dir and add hw5 own cmakelists --- CMakeLists.txt | 1 + homework_05/CMakeLists.txt | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 homework_05/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 79f396b..19f96e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. add_subdirectory(homework_04) +add_subdirectory(homework_05) # Demo-код для заняття 2.4: локальне і віддалене відлагодження через GDB. add_subdirectory(demos/lesson_2_4/debug_probe) diff --git a/homework_05/CMakeLists.txt b/homework_05/CMakeLists.txt new file mode 100644 index 0000000..3ef0de7 --- /dev/null +++ b/homework_05/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.20) +project(telemetry_check CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_library(telemetry STATIC src/telemetry.cpp) +target_include_directories(telemetry PUBLIC include) + +add_executable(telemetry_check src/main.cpp) +target_link_libraries(telemetry_check PRIVATE telemetry) From 960672b84096435755c876d0c7092065c2122698 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 6 May 2026 18:44:20 +0000 Subject: [PATCH 2/9] hw5 debug vscode lauch config --- .vscode/launch.json | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 7e724e0..e0bf49c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -51,6 +51,30 @@ "ignoreFailures": true } ] + }, + { + // Локальний debug стартера ДЗ 5. preLaunchTask збирає debug preset. + "name": "Debug homework_05: telemetry_check", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug/homework_05/telemetry_check", + "args": [ + "${workspaceFolder}/homework_05/data/good.txt" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "preLaunchTask": "CMake: configure and build", + "setupCommands": [ + { + "description": "Увімкнути pretty-printing для STL типів у GDB.", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] } ] -} +} \ No newline at end of file From 3102754cb449e77cca7276f10cdade65d861bdb7 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 6 May 2026 19:07:29 +0000 Subject: [PATCH 3/9] handle empty file and missing params --- homework_05/include/telemetry.hpp | 1 + homework_05/src/main.cpp | 4 ++++ homework_05/src/telemetry.cpp | 24 ++++++++++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/homework_05/include/telemetry.hpp b/homework_05/include/telemetry.hpp index 0cdf002..5fd6d4e 100644 --- a/homework_05/include/telemetry.hpp +++ b/homework_05/include/telemetry.hpp @@ -12,6 +12,7 @@ struct Frame { double temperature_c; int gps_fix; int satellites; + bool error; }; // Aggregated values printed by the executable. diff --git a/homework_05/src/main.cpp b/homework_05/src/main.cpp index 43ec461..a9a2394 100644 --- a/homework_05/src/main.cpp +++ b/homework_05/src/main.cpp @@ -12,6 +12,10 @@ int main(int argc, char** argv) { Frame frames[MAX_TELEMETRY_FRAMES]; const int frame_count = read_frames(argv[1], frames, MAX_TELEMETRY_FRAMES); + if (frame_count == -1) { + return 0; + } + const Summary summary = summarize(frames, frame_count); print_summary(summary); diff --git a/homework_05/src/telemetry.cpp b/homework_05/src/telemetry.cpp index 9ac87fc..483720f 100644 --- a/homework_05/src/telemetry.cpp +++ b/homework_05/src/telemetry.cpp @@ -64,12 +64,22 @@ double parse_double(const char* text) { return value; } -Frame parse_frame(char line[]) { +Frame parse_frame(char line[], int frame_index) { char* fields[EXPECTED_FIELD_COUNT] = {}; const int field_count = split_line(line, fields, EXPECTED_FIELD_COUNT); (void)field_count; Frame frame{}; + + for (int i = 0; i < EXPECTED_FIELD_COUNT; ++i) { + if (fields[i] == nullptr) { + std::cerr << "error: missing field " << i + 1 << " in frame " << frame_index + 1 << '\n'; + frame.error = true; + + return frame; + } + } + frame.timestamp_ms = parse_long(fields[0]); frame.seq = parse_int(fields[1]); frame.voltage_v = parse_double(fields[2]); @@ -102,11 +112,21 @@ int read_frames(const char* path, Frame frames[], int max_frames) { } if (frame_count < max_frames) { - frames[frame_count] = parse_frame(line); + frames[frame_count] = parse_frame(line, frame_count); + + if (frames[frame_count].error) { + return -1; + } + ++frame_count; } } + if (frame_count == 0) { + std::cerr << "error: zero telemetery frames provided in file: " << path << std::endl; + return -1; + } + return frame_count; } From 77f4882138dd13600b1ba7c8fcccb53161bb28be Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 6 May 2026 19:18:07 +0000 Subject: [PATCH 4/9] handle bad invalid number case --- homework_05/src/telemetry.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/homework_05/src/telemetry.cpp b/homework_05/src/telemetry.cpp index 483720f..a54a6f7 100644 --- a/homework_05/src/telemetry.cpp +++ b/homework_05/src/telemetry.cpp @@ -43,7 +43,7 @@ long parse_long(const char* text) { const long value = std::strtol(text, &end, 10); if (end == text) { - std::abort(); + throw std::invalid_argument("invalid long value: \"" + std::string(text) + '"'); } return value; @@ -58,7 +58,7 @@ double parse_double(const char* text) { const double value = std::strtod(text, &end); if (end == text) { - std::abort(); + throw std::invalid_argument("invalid double value: \"" + std::string(text) + '"'); } return value; @@ -80,13 +80,19 @@ Frame parse_frame(char line[], int frame_index) { } } - frame.timestamp_ms = parse_long(fields[0]); - frame.seq = parse_int(fields[1]); - frame.voltage_v = parse_double(fields[2]); - frame.current_a = parse_double(fields[3]); - frame.temperature_c = parse_double(fields[4]); - frame.gps_fix = parse_int(fields[5]); - frame.satellites = parse_int(fields[6]); + try { + frame.timestamp_ms = parse_long(fields[0]); + frame.seq = parse_int(fields[1]); + frame.voltage_v = parse_double(fields[2]); + frame.current_a = parse_double(fields[3]); + frame.temperature_c = parse_double(fields[4]); + frame.gps_fix = parse_int(fields[5]); + frame.satellites = parse_int(fields[6]); + } catch (const std::invalid_argument& e) { + std::cerr << "error: " << e.what() << " in frame " << frame_index + 1 << '\n'; + frame.error = true; + } + return frame; } From c90d5c3541db37d59c6500a929fb5d453c30046b Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 6 May 2026 19:25:40 +0000 Subject: [PATCH 5/9] should return non zero code on input data errors --- homework_05/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework_05/src/main.cpp b/homework_05/src/main.cpp index a9a2394..b977f1f 100644 --- a/homework_05/src/main.cpp +++ b/homework_05/src/main.cpp @@ -13,7 +13,7 @@ int main(int argc, char** argv) { const int frame_count = read_frames(argv[1], frames, MAX_TELEMETRY_FRAMES); if (frame_count == -1) { - return 0; + return -1; } const Summary summary = summarize(frames, frame_count); From 01716b2d9b0788118ec3166b1eb69a3e35f612c6 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 6 May 2026 19:47:17 +0000 Subject: [PATCH 6/9] better validations and more data integrity checks --- homework_05/src/telemetry.cpp | 50 +++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/homework_05/src/telemetry.cpp b/homework_05/src/telemetry.cpp index a54a6f7..a534384 100644 --- a/homework_05/src/telemetry.cpp +++ b/homework_05/src/telemetry.cpp @@ -11,6 +11,7 @@ const int EXPECTED_FIELD_COUNT = 7; const int MAX_LINE_LENGTH = 256; +const int TEMP_RANGE[] = {-40, 120}; int split_line(char line[], char* fields[], int max_fields) { int count = 0; @@ -73,7 +74,7 @@ Frame parse_frame(char line[], int frame_index) { for (int i = 0; i < EXPECTED_FIELD_COUNT; ++i) { if (fields[i] == nullptr) { - std::cerr << "error: missing field " << i + 1 << " in frame " << frame_index + 1 << '\n'; + std::cerr << "error: invalid frame at line " << frame_index + 1 << ": missing field " << std::endl; frame.error = true; return frame; @@ -89,7 +90,7 @@ Frame parse_frame(char line[], int frame_index) { frame.gps_fix = parse_int(fields[5]); frame.satellites = parse_int(fields[6]); } catch (const std::invalid_argument& e) { - std::cerr << "error: " << e.what() << " in frame " << frame_index + 1 << '\n'; + std::cerr << "error: invalid frame " << frame_index + 1 << ": " << e.what() << std::endl; frame.error = true; } @@ -119,6 +120,51 @@ int read_frames(const char* path, Frame frames[], int max_frames) { if (frame_count < max_frames) { frames[frame_count] = parse_frame(line, frame_count); + if (frame_count > 0) { + if (frames[frame_count].seq != frames[frame_count - 1].seq + 1) { + std::cerr << "error: invalid frame at line " << frame_count + 1 + << ": non-sequential frame index " << frames[frame_count].seq + << " after " << frames[frame_count - 1].seq << std::endl; + frames[frame_count].error = true; + } + + if (frames[frame_count].timestamp_ms <= frames[frame_count - 1].timestamp_ms) { + std::cerr << "error: invalid frame at line " << frame_count + 1 + << ": non-monotonic timestamp " << frames[frame_count].timestamp_ms + << " after " << frames[frame_count - 1].timestamp_ms << std::endl; + frames[frame_count].error = true; + } + + if (frames[frame_count].voltage_v <= 0.0) { + std::cerr << "error: invalid frame at line " << frame_count + 1 + << ": voltage must be positive, got " << frames[frame_count].voltage_v + << std::endl; + frames[frame_count].error = true; + } + + if (frames[frame_count].temperature_c < TEMP_RANGE[0] || + frames[frame_count].temperature_c > TEMP_RANGE[1]) { + std::cerr << "error: invalid frame at line " << frame_count + 1 + << ": temperature out of range [" << TEMP_RANGE[0] << ", " + << TEMP_RANGE[1] << "], got " << frames[frame_count].temperature_c + << std::endl; + frames[frame_count].error = true; + } + + if (frames[frame_count].gps_fix != 0 && frames[frame_count].gps_fix != 1) { + std::cerr << "error: invalid frame at line " << frame_count + 1 + << ": gps_fix must be in range [0, 1], got " << frames[frame_count].gps_fix + << std::endl; + frames[frame_count].error = true; + } + + if (frames[frame_count].satellites < 0) { + std::cerr << "error: invalid frame at line " << frame_count + 1 + << ": satellites must be non-negative, got " << frames[frame_count].satellites + << std::endl; + frames[frame_count].error = true; + } + } if (frames[frame_count].error) { return -1; From 73f9529042c9fd74560981cc05f2954ce1aa9dc2 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Thu, 7 May 2026 11:14:08 +0000 Subject: [PATCH 7/9] add release and relwithdebinfo presets --- CMakePresets.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CMakePresets.json b/CMakePresets.json index f7f4f45..9fb8554 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -26,6 +26,26 @@ "CMAKE_BUILD_TYPE": "Debug", "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/toolchains/aarch64-linux-gnu.cmake" } + }, + { + "name": "release", + "displayName": "Release", + "description": "Release build без debug символів.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "relwithdebinfo", + "displayName": "Release with Debug Info", + "description": "Release build із debug символами.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/relwithdebinfo", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } } ], "buildPresets": [ @@ -40,6 +60,18 @@ "displayName": "Build ARM64 Debug", "description": "Зібрати ARM64 debug preset після cross-configure.", "configurePreset": "aarch64-debug" + }, + { + "name": "release", + "displayName": "Build Release", + "description": "Зібрати release preset після configure.", + "configurePreset": "release" + }, + { + "name": "relwithdebinfo", + "displayName": "Build RelWithDebInfo", + "description": "Зібрати relwithdebinfo preset після configure.", + "configurePreset": "relwithdebinfo" } ] } From 023d3043e98623869a6d32e40337dcdf07120e6b Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 20 May 2026 07:06:54 +0000 Subject: [PATCH 8/9] do not crash on opening inexistent file --- homework_05/src/telemetry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework_05/src/telemetry.cpp b/homework_05/src/telemetry.cpp index a534384..0749b4c 100644 --- a/homework_05/src/telemetry.cpp +++ b/homework_05/src/telemetry.cpp @@ -107,7 +107,7 @@ int read_frames(const char* path, Frame frames[], int max_frames) { std::ifstream input{path}; if (!input) { std::cerr << "error: failed to open input file: " << path << '\n'; - return 0; + return -1; } int frame_count = 0; From 63c9ae94c0b8a6f7c1d2bddeaa6a3e251bd1a2d0 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Wed, 20 May 2026 07:10:06 +0000 Subject: [PATCH 9/9] proper exit code --- homework_05/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework_05/src/main.cpp b/homework_05/src/main.cpp index b977f1f..b284e0b 100644 --- a/homework_05/src/main.cpp +++ b/homework_05/src/main.cpp @@ -13,7 +13,7 @@ int main(int argc, char** argv) { const int frame_count = read_frames(argv[1], frames, MAX_TELEMETRY_FRAMES); if (frame_count == -1) { - return -1; + return 1; } const Summary summary = summarize(frames, frame_count);