Skip to content

Commit

Permalink
Merge pull request #81 from klonyyy/csv_streamer
Browse files Browse the repository at this point in the history
CSV streamer
  • Loading branch information
klonyyy authored Oct 3, 2024
2 parents e5332bb + 0110419 commit 0f21f84
Show file tree
Hide file tree
Showing 36 changed files with 455 additions and 81 deletions.
7 changes: 7 additions & 0 deletions .github/ISSUE_TEMPLATE/issue_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ body:
placeholder: "e.g. ST-Link V2, ST-Link V3 MINIE, J-Link Ultra"
validations:
required: true
- type: input
id: MCU
attributes:
label: "Microcontroller:"
placeholder: "e.g. STM32G474CC (ST), LPC1768FBD100 (NXP)"
validations:
required: true
- type: textarea
id: issue_description
attributes:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ test/MCUViewer_test/.settings/
# Executables
*.out
*.app

# Log files
*.csv
39 changes: 28 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ endif()

project(MCUViewer)

set(MCUVIEWER_VERSION 1.0.0)

set(CMAKE_BUILD_TYPE Release)
set(MCUVIEWER_VERSION 1.0.1)
set(CMAKE_CXX_STANDARD 20)

set(CMAKE_EXPORT_COMPILE_COMMANDS 1)

if(PRODUCTION)
set(CMAKE_BUILD_TYPE Release)
message("Building for production!")
else()
set(CMAKE_BUILD_TYPE Debug)
endif()

if(WIN32 AND "${PLATFORM}" STREQUAL "native")
set(LLVM_DIR "C:/msys64/mingw64/bin")
set(CMAKE_PREFIX_PATH ${LLVM_DIR} ${CMAKE_PREFIX_PATH})
Expand All @@ -33,8 +37,8 @@ if(WIN32 AND "${PLATFORM}" STREQUAL "native")
endif()

if(CMAKE_BUILD_TYPE STREQUAL "ReleaseWithDbg")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g1")
endif()

add_compile_options(-Wall
Expand All @@ -44,6 +48,10 @@ add_compile_options(-Wall
-Wno-unused-parameter
-Wno-missing-field-initializers)

if(WIN32)
add_compile_options(-Wa,-mbig-obj)
endif()

set(JLINK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/jlink/inc/)

if(UNIX)
Expand Down Expand Up @@ -74,6 +82,9 @@ if(UNIX)
set(LIB_INSTALL_PATH /usr/local/lib)
set(DESKTOP_FILE_PATH /usr/share/applications)

set(LIB_CONF_FILE ${CMAKE_CURRENT_SOURCE_DIR}/launch/install/Unix/MCUViewer.conf)
set(LIB_CONF_FILE_PATH /etc/ld.so.conf.d/)

if(APPLE)
find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED)
Expand All @@ -87,7 +98,7 @@ endif()

if(WIN32)
enable_language("RC")
set(ICON_RC "${CMAKE_CURRENT_SOURCE_DIR}/launch/icon.rc")
set(ICON_RC "${CMAKE_CURRENT_SOURCE_DIR}/launch/install/Windows/icon.rc")
set(GLFW3_WINDOWS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/GLFW/lib/windows/glfw3.dll)
set(STLINK_WINDOWS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/stlink/lib/windows/libstlink.a)
set(JLINK_WINDOWS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/jlink/lib/windows/JLink_x64.dll)
Expand Down Expand Up @@ -124,7 +135,8 @@ set(PROJECT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/TraceReader/TraceReader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/TraceReader/StlinkTraceProbe.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/TraceReader/JlinkTraceProbe.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/GdbParser/GdbParser.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/src/GdbParser/GdbParser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/CSVStreamer/CSVStreamer.cpp)

set(IMGUI_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/third_party/imgui/imgui.cpp
Expand Down Expand Up @@ -188,7 +200,8 @@ target_include_directories(${EXECUTABLE} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/TraceReader
${CMAKE_CURRENT_SOURCE_DIR}/src/RingBuffer
${CMAKE_CURRENT_SOURCE_DIR}/src/Statistics
${CMAKE_CURRENT_SOURCE_DIR}/src/GdbParser)
${CMAKE_CURRENT_SOURCE_DIR}/src/GdbParser
${CMAKE_CURRENT_SOURCE_DIR}/src/CSVStreamer)

target_include_directories(${EXECUTABLE} SYSTEM PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/third_party/stlink/inc/
Expand Down Expand Up @@ -260,13 +273,16 @@ if(WIN32)
endif()

if(UNIX)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/launch/icon.png DESTINATION ${INSTALL_PATH})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/launch/MCUViewer.desktop
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/launch/install/Unix/icon.png DESTINATION ${INSTALL_PATH})
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/launch/install/Unix/MCUViewer.desktop
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${DESKTOP_FILE_PATH})
install(FILES ${JLINK_LINUX}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${LIB_INSTALL_PATH})
install(FILES ${LIB_CONF_FILE}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
DESTINATION ${LIB_CONF_FILE_PATH})
set(CPACK_GENERATOR "DEB;RPM")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libglfw3 | libglfw3-wayland, libgl1, libglib2.0-0, libgtk-3-0, libstdc++6, libusb-1.0-0, gdb")
endif()
Expand All @@ -283,4 +299,5 @@ set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
set(CPACK_NSIS_MODIFY_PATH ON)
set(CPACK_ALL_INSTALL_TYPES Full Developer)
set(CPACK_COMPONENT_APPLICATIONS_INSTALL_TYPES Full)
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${PROJECT_SOURCE_DIR}/launch/install/Unix/postinst")
include(CPack)
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ It works similar with other probes such as JLink, so be sure to check the maximu
Example project with MCUViewer config file is located in test/MCUViewer_test directory.

FAQ and common issues:

1. Problem: My trace doesn't look like it's supposed to and I get a lot of error frames
Answer: Try lowering the trace prescaller and check the SWO pin connection - the SWO pin output is high frequency and it shouldn't be too long.

Expand All @@ -100,6 +101,7 @@ Answer: Try logging fewer channels simultaneously. It could be that you've satur
3. Problem: My trace looks like it's supposed to but I get the "delayed timestamp 1" indicator
Answer: This is not a critical error, however, you should be cautious as some of the trace frames may be delayed. To fix try logging fewer channels simultaneously.

Please remember that although SWO is ARM standardized, there might be some differences the setup process. It should work without problems in most cases, but some MCUs might require some additional steps. Please see the [SEGGER's wiki page](https://wiki.segger.com/SWO) for more information.

## Building

Expand Down
12 changes: 10 additions & 2 deletions example/STMViewer_test/MCUViewer_project/MCUViewer_test.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ file_path = ../example/MCUViewer_test/Debug/MCUViewer_test.elf

[settings]
version = 0
sample_frequency_hz = 1000
sample_frequency_hz = 1000000
max_points = 10000
max_viewport_points = 5000
refresh_on_elf_change = true
stop_acq_on_elf_change = true
probe_type = 1
target_name = STM32G474CC
probe_mode = 1
probe_speed_khz = 50000
probe_speed_khz = 20000
probe_sn = 506003225
should_log = false
log_directory =

[trace_settings]
core_frequency = 150000
Expand All @@ -22,6 +24,12 @@ max_viewport_points_percent = 10
trigger_channel = -1
trigger_level = 0.000000
timeout = 2
probe_type = 1
target_name = STM32G474CC
probe_speed_khz = 10000
probe_sn = 506003225
should_log = true
log_directory = C:/Users/klonyyy/PROJECTS/MCUViewer_/MCUViewer/example/STMViewer_test/MCUViewer_project

[var0]
name = test.a
Expand Down
1 change: 1 addition & 0 deletions launch/install/Unix/MCUViewer.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/usr/local/lib
File renamed without changes.
File renamed without changes
File renamed without changes.
3 changes: 3 additions & 0 deletions launch/install/Unix/postinst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
echo "Running postinstall script..."
sudo ldconfig
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
109 changes: 109 additions & 0 deletions src/CSVStreamer/CSVStreamer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "CSVStreamer.hpp"

#include <fstream>
#include <future>
#include <iostream>
#include <string>

bool CSVStreamer::Buffer::appendLine(std::string& line)
{
if (isFull())
return false;

buffer[index] = line;
index++;
return true;
}

bool CSVStreamer::Buffer::isFull() const
{
return (index + 1) >= buffer.size();
}

CSVStreamer::CSVStreamer(spdlog::logger* logger) : logger(logger)
{
currentBuffer = &buffer1;
buffer1.nextBuffer = &buffer2;
buffer2.nextBuffer = &buffer1;
}

CSVStreamer::~CSVStreamer()
{
finishLogging();
}

bool CSVStreamer::prepareFile(std::string& directory)
{
filePath = directory + logFileName;
csvFile.open(filePath, std::ios::out);
if (!csvFile.is_open())
{
logger->error("Failed to open file: {}", filePath);
return false;
}
return true;
}

void CSVStreamer::createHeader(const std::vector<std::string>& values)
{
std::string header = "time,";
for (const auto& value : values)
{
header += value + ",";
}
header.back() = '\n';
currentBuffer->appendLine(header);
}

void CSVStreamer::writeLine(double time, const std::vector<double>& values)
{
std::string line = std::to_string(time) + ",";
for (const auto& value : values)
{
line += std::to_string(value) + ",";
}
line.back() = '\n';

currentBuffer->appendLine(line);

if (currentBuffer->isFull())
{
exchangeBuffers();

if (saveTask.valid() && saveTask.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
logger->error("Buffer overrun in CSVStreamer object!");

saveTask = std::async(std::launch::async, &CSVStreamer::writeFile, this);
}
}

void CSVStreamer::exchangeBuffers()
{
processingBuffer = currentBuffer;
currentBuffer = currentBuffer->nextBuffer;
currentBuffer->index = 0;
}

void CSVStreamer::writeFile()
{
if (!csvFile.is_open())
{
logger->error("CSV file is not open!");
return;
}

for (size_t i = 0; i < processingBuffer->index; i++)
{
csvFile << processingBuffer->buffer[i];
}
}

void CSVStreamer::finishLogging()
{
if (csvFile.is_open())
{
exchangeBuffers();
writeFile();
csvFile.close();
}
}
63 changes: 63 additions & 0 deletions src/CSVStreamer/CSVStreamer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef _CSV_STREAMER_HPP
#define _CSV_STREAMER_HPP

#include <fstream>
#include <future>
#include <iostream>
#include <string>

#include "spdlog/spdlog.h"

class CSVStreamer
{
public:
struct Buffer
{
bool appendLine(std::string& line);
bool isFull() const;

std::array<std::string, 1000> buffer;
size_t index;
Buffer* nextBuffer;
};

CSVStreamer(spdlog::logger* logger);
~CSVStreamer();

/// @brief Creates file in given directory with a fixed name
/// @param directory directory string
/// @return
bool prepareFile(std::string& directory);

/// @brief create csv file header from given argument, first column - time - is added internally
/// @param values table headers
void createHeader(const std::vector<std::string>& values);

/// @brief writes single line to internal buffer
/// @param time
/// @param values
void writeLine(double time, const std::vector<double>& values);

/// @brief exchanges the buffer that is being processed with the one that's being written to
void exchangeBuffers();

/// @brief writes the processing buffer to the opened csv file
void writeFile();

/// @brief writes the rest of the buffer to the file and closes it
void finishLogging();

private:
const char* logFileName = "/logfile.csv";

spdlog::logger* logger;
std::future<void> saveTask{};
std::string filePath;
std::ofstream csvFile;
Buffer buffer1{};
Buffer buffer2{};
Buffer* currentBuffer;
Buffer* processingBuffer;
};

#endif
Loading

0 comments on commit 0f21f84

Please sign in to comment.