Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
]
}
}
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
32 changes: 32 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand All @@ -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"
}
]
}
12 changes: 12 additions & 0 deletions homework_05/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions homework_05/include/telemetry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct Frame {
double temperature_c;
int gps_fix;
int satellites;
bool error;
};

// Aggregated values printed by the executable.
Expand Down
4 changes: 4 additions & 0 deletions homework_05/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для failed-open path краще обробляти стан помилки окремо від кількості frame-ів. Зараз main перевіряє тільки -1, тому missing file після failed to open доходить до summarize і падає з Floating point exception.

Copy link
Copy Markdown
Owner Author

@mandelbroo mandelbroo May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

При спробі відкриття неіснуючого файлу забув повертати -1.
Просто не хотілося додавати якісь стуктури. Як варіант хіба що try catch

return 1;
}

const Summary summary = summarize(frames, frame_count);
print_summary(summary);

Expand Down
96 changes: 84 additions & 12 deletions homework_05/src/telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -43,7 +44,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;
Expand All @@ -58,25 +59,41 @@ 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;
}

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{};
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]);

for (int i = 0; i < EXPECTED_FIELD_COUNT; ++i) {
if (fields[i] == nullptr) {
std::cerr << "error: invalid frame at line " << frame_index + 1 << ": missing field " << std::endl;
frame.error = true;

return frame;
}
}

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: invalid frame " << frame_index + 1 << ": " << e.what() << std::endl;
frame.error = true;
}

return frame;
}

Expand All @@ -90,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;
Expand All @@ -102,11 +119,66 @@ 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 (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;
}

++frame_count;
}
}

if (frame_count == 0) {
std::cerr << "error: zero telemetery frames provided in file: " << path << std::endl;
return -1;
}

return frame_count;
}

Expand Down
Loading