Compare commits

...

10 Commits

Author SHA1 Message Date
Sven Czarnian
5da73b0b52 Merge branch 'feature/configuration' into 'develop'
Feature/configuration

See merge request nav/aman-es!2
2021-08-11 06:31:08 +00:00
Sven Czarnian
f188b85dcd load the configuration files 2021-08-11 08:29:38 +02:00
Sven Czarnian
fad3ca0de1 add a log message and find the path of the DLL to load the configurations later 2021-08-11 08:29:14 +02:00
Sven Czarnian
ac022f55fc add the configuration to the plugin 2021-08-11 08:27:51 +02:00
Sven Czarnian
272fb3aab0 add the communication file format 2021-08-10 08:42:53 +02:00
Sven Czarnian
3c4acffbb9 define the configuration to handle communication settings 2021-08-10 08:42:36 +02:00
Sven Czarnian
81edcdcea2 add a parser to read the UID per user 2021-08-10 08:42:00 +02:00
Sven Czarnian
163786698c define the base class to define different file formats 2021-08-10 08:40:53 +02:00
Sven Czarnian
5543d28bbf add GSL as a dependency 2021-08-10 08:39:45 +02:00
Sven Czarnian
4de1e2fc43 add an helper class to process strings 2021-08-10 08:39:34 +02:00
11 changed files with 578 additions and 2 deletions

View File

@@ -0,0 +1,61 @@
/*
* @brief Defines the communication file format
* @file aman/config/CommunicationFileFormat.h
* @author Sven Czarnian <devel@svcz.de>
* @copyright Copyright 2021 Sven Czarnian
* @license This project is published under the GNU General Public License v3 (GPLv3)
*/
#pragma once
#include <aman/config/FileFormat.h>
#include <aman/types/Communication.h>
namespace aman {
/**
* @brief Defines the parser of communication configurations
* @ingroup format
*
* All configuration entries need to be stored in 'AmanCommunication.txt'.
*
* The following entries of the configuration exist:
* <table>
* <caption id="multi_row">Setting entries</caption>
* <tr>
* <th>Name</th><th>Description</th><th>Default value</th><th>Unit</th>
* </tr>
* <tr>
* <td>URL</td>
* <td>Defines the URL to the backend.</td>
* <td></td><td></td>
* </tr>
* <tr>
* <td>Port</td>
* <td>Defines the port to access the backend.</td>
* <td></td><td></td>
* </tr>
* <tr>
* <td>UID</td>
* <td>Defines the identifier to access the backend.</td>
* <td></td><td></td>
* </tr>
* </table>
*/
class CommunicationFileFormat : public FileFormat {
#ifndef DOXYGEN_IGNORE
public:
/**
* @brief Initializes the communication file format
*/
CommunicationFileFormat();
/**
* @brief Parses the set configuration file
* @param[in] filename The path to the configuration file
* @param[out] config The resulting configuration
* @return True if the configuration file was valid, else false
*/
bool parse(const std::string& filename, Communication& config);
#endif
};
}

View File

@@ -0,0 +1,53 @@
/*
* @brief Defines the base class for all file formats
* @file aman/config/FileFormat.h
* @author Sven Czarnian <devel@svcz.de>
* @copyright Copyright 2021 Sven Czarnian
* @license This project is published under the GNU General Public License v3 (GPLv3)
*/
#pragma once
#include <cstdint>
#include <string>
namespace aman {
/**
* @brief Defines the base class for all file formats
* @ingroup format
*
* The base class provides error information, etc.
*/
class FileFormat {
protected:
std::uint32_t m_errorLine; /**< Defines the line number if a parser detected an error */
std::string m_errorMessage; /**< Defines the descriptive error message */
/**
* @brief Creates the default file format
*/
FileFormat() noexcept;
/**
* @brief Resets the internal structures
*/
void reset() noexcept;
public:
/**
* @brief Checks if an error was found
* @return True if an error was found, else false
*/
bool errorFound() const noexcept;
/**
* @brief Returns the error line
* @return The error line
*/
std::uint32_t errorLine() const noexcept;
/**
* @brief Returns the error message
* @return The error message
*/
const std::string& errorMessage() const noexcept;
};
}

View File

@@ -0,0 +1,51 @@
/*
* @brief Defines the communication file format
* @file aman/config/CommunicationFileFormat.h
* @author Sven Czarnian <devel@svcz.de>
* @copyright Copyright 2021 Sven Czarnian
* @license This project is published under the GNU General Public License v3 (GPLv3)
*/
#pragma once
#include <aman/config/FileFormat.h>
#include <aman/types/Communication.h>
namespace aman {
/**
* @brief Defines the parser of communication configurations
* @ingroup format
*
* All configuration entries need to be stored in 'AmanCommunication.txt'.
*
* The following entries of the configuration exist:
* <table>
* <caption id="multi_row">Setting entries</caption>
* <tr>
* <th>Name</th><th>Description</th><th>Default value</th><th>Unit</th>
* </tr>
* <tr>
* <td>UID</td>
* <td>Defines the identifier to access the backend.</td>
* <td></td><td></td>
* </tr>
* </table>
*/
class IdentifierFileFormat : public FileFormat {
#ifndef DOXYGEN_IGNORE
public:
/**
* @brief Initializes the identifier file format
*/
IdentifierFileFormat();
/**
* @brief Parses the set configuration file
* @param[in] filename The path to the configuration file
* @param[out] config The resulting configuration
* @return True if the configuration file was valid, else false
*/
bool parse(const std::string& filename, Communication& config);
#endif
};
}

View File

@@ -0,0 +1,86 @@
/*
* @brief Defines and implements functions to handle strings
* @file aman/helper/String.h
* @author Sven Czarnian <devel@svcz.de>
* @copyright Copyright 2021 Sven Czarnian
* @license This project is published under the GNU General Public License v3 (GPLv3)
*/
#pragma once
#include <string>
#include <vector>
namespace aman {
/**
* @brief Implements and defines convinience functions for the string handling
* @ingroup helper
*/
class String {
private:
template <typename Separator>
static auto splitAux(const std::string& value, Separator&& separator) -> std::vector<std::string> {
std::vector<std::string> result;
std::string::size_type p = 0;
std::string::size_type q;
while ((q = separator(value, p)) != std::string::npos) {
result.emplace_back(value, p, q - p);
p = q + 1;
}
result.emplace_back(value, p);
return result;
}
public:
String() = delete;
String(const String&) = delete;
String(String&&) = delete;
String& operator=(const String&) = delete;
String& operator=(String&&) = delete;
/**
* @brief Replaces all markers by replace in message
* @param[in,out] message The message which needs to be modified
* @param[in] marker The wildcard which needs to be found in message and which needs to be replaced
* @param[in] replace The replacement of marker in message
* @return
*/
static __inline void stringReplace(std::string& message, const std::string& marker, const std::string& replace) {
std::size_t pos = message.find(marker, 0);
while (std::string::npos != pos) {
auto it = message.cbegin() + pos;
message.replace(it, it + marker.length(), replace);
pos = message.find(marker, pos + marker.length());
}
}
/**
* @brief Splits value into chunks and the separator is defined in separators
* @param[in] value The string which needs to be splitted up
* @param[in] separators The separators which split up the value
* @return The list of splitted chunks
*/
static auto splitString(const std::string& value, const std::string& separators) -> std::vector<std::string> {
return String::splitAux(value, [&](const std::string& v, std::string::size_type p) noexcept {
return v.find_first_of(separators, p);
});
}
/**
* @brief Removes leading and trailing whitespaces
* @param[in] value The trimable string
* @param[in] spaces The characters that need to be removed
* @return The trimmed version of value
*/
static auto trim(const std::string& value, const std::string& spaces = " \t") -> std::string {
const auto begin = value.find_first_not_of(spaces, 0);
if (std::string::npos == begin)
return "";
const auto end = value.find_last_not_of(spaces);
const auto range = end - begin + 1;
return value.substr(begin, range);
}
};
}

View File

@@ -0,0 +1,33 @@
/*
* @brief Defines the communication configuration
* @file aman/types/Communication.h
* @author Sven Czarnian <devel@svcz.de>
* @copyright Copyright 2021 Sven Czarnian
* @license This project is published under the GNU General Public License v3 (GPLv3)
*/
#pragma once
#include <string>
namespace aman {
/**
* @brief Defines the communication structure
* @ingroup types
*/
struct Communication {
bool valid; /**< Marks if the configuration is valid */
std::string url; /**< The URL of the backend */
std::uint16_t port; /**< The port of the backend */
std::string identifier; /**< The user's identifier for the connection */
/**
* @brief Initializes an invalid configuration
*/
Communication() :
valid(false),
url(),
port(0),
identifier() { }
};
}

View File

@@ -14,22 +14,42 @@ SET(SOURCE_FILES
stdafx.cpp
stdafx.h
)
SET(SOURCE_CONFIG_FILES
config/CommunicationFileFormat.cpp
config/FileFormat.cpp
config/IdentifierFileFormat.cpp
)
SET(SOURCE_FILES_RES
${CMAKE_BINARY_DIR}/ArrivalMANager.rc
${CMAKE_SOURCE_DIR}/res/resource.h
${CMAKE_SOURCE_DIR}/res/targetver.h
)
SET(INCLUDE_CONFIG_FILES
${CMAKE_SOURCE_DIR}/include/aman/config/CommunicationFileFormat.h
${CMAKE_SOURCE_DIR}/include/aman/config/FileFormat.h
${CMAKE_SOURCE_DIR}/include/aman/config/IdentifierFileFormat.h
)
SET(INCLUDE_HELPER_FILES
${CMAKE_SOURCE_DIR}/include/aman/helper/String.h
)
SET(INCLUDE_TYPES_FILES
${CMAKE_SOURCE_DIR}/include/aman/types/Communication.h
)
# define the plug in
ADD_LIBRARY(
ArrivalMANager SHARED
${SOURCE_FILES_RES}
${SOURCE_CONFIG_FILES}
${SOURCE_FILES}
${INCLUDE_CONFIG_FILES}
${INCLUDE_HELPER_FILES}
${INCLUDE_TYPES_FILES}
)
# define the dependencies
TARGET_INCLUDE_DIRECTORIES(ArrivalMANager INTERFACE EuroScope)
TARGET_LINK_LIBRARIES(ArrivalMANager EuroScope)
TARGET_LINK_LIBRARIES(ArrivalMANager EuroScope GSL Shlwapi.lib)
# configure the debugger and update the linker flags
IF(MSVC)
@@ -45,4 +65,8 @@ IF(MSVC)
ENDIF()
SOURCE_GROUP("Source Files" FILES ${SOURCE_FILES})
SOURCE_GROUP("Source Files\\config" FILES ${SOURCE_CONFIG_FILES})
SOURCE_GROUP("Source Files\\res" FILES ${SOURCE_FILES_RES})
SOURCE_GROUP("Header Files\\config" FILES ${INCLUDE_CONFIG_FILES})
SOURCE_GROUP("Header Files\\helper" FILES ${INCLUDE_HELPER_FILES})
SOURCE_GROUP("Header Files\\types" FILES ${INCLUDE_TYPES_FILES})

View File

@@ -11,8 +11,17 @@
#include "stdafx.h"
#include <gsl/gsl>
#include <Shlwapi.h>
#include <Windows.h>
#include <aman/config/CommunicationFileFormat.h>
#include <aman/config/IdentifierFileFormat.h>
#include "PlugIn.h"
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
using namespace aman;
PlugIn::PlugIn() :
@@ -20,6 +29,30 @@ PlugIn::PlugIn() :
PLUGIN_NAME,
PLUGIN_VERSION,
PLUGIN_DEVELOPER,
PLUGIN_COPYRIGHT) { }
PLUGIN_COPYRIGHT),
m_configuration() {
this->DisplayUserMessage(PLUGIN_NAME, "INFO", (std::string("Loaded ") + PLUGIN_NAME + " " + PLUGIN_VERSION).c_str(), true, true, false, false, false);
/* get the dll-path */
char path[MAX_PATH + 1] = { 0 };
const gsl::span<char, MAX_PATH + 1> span(path);
GetModuleFileNameA((HINSTANCE)&__ImageBase, span.data(), span.size());
PathRemoveFileSpecA(span.data());
std::string dllPath = span.data();
CommunicationFileFormat comFormat;
if (false == comFormat.parse(dllPath + "\\AmanCommunication.txt", this->m_configuration) || true == comFormat.errorFound()) {
this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to parse AmanCommunication.txt", true, true, true, true, true);
this->DisplayUserMessage(PLUGIN_NAME, "ERROR", ("AmanCommunication.txt:" + std::to_string(comFormat.errorLine()) + " - " + comFormat.errorMessage()).c_str(), true, true, true, true, true);
return;
}
IdentifierFileFormat identFormat;
if (false == identFormat.parse(dllPath + "\\AmanIdentity.txt", this->m_configuration) || true == identFormat.errorFound()) {
this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to parse AmanIdentity.txt", true, true, true, true, true);
this->DisplayUserMessage(PLUGIN_NAME, "ERROR", ("AmanIdentity.txt:" + std::to_string(identFormat.errorLine()) + " - " + identFormat.errorMessage()).c_str(), true, true, true, true, true);
return;
}
}
PlugIn::~PlugIn() noexcept { }

View File

@@ -14,6 +14,8 @@
#include <EuroScopePlugIn.h>
#pragma warning(pop)
#include <aman/types/Communication.h>
namespace aman {
/**
* @brief Defines the EuroScope plug-in
@@ -31,6 +33,8 @@ namespace aman {
enum class TagItemElement {
};
Communication m_configuration;
public:
/**
* @brief Creates a new plug-in

View File

@@ -0,0 +1,100 @@
/*
* Author:
* Sven Czarnian <devel@svcz.de>
* Brief:
* Implements the communication file format
* Copyright:
* 2021 Sven Czarnian
* License:
* GNU General Public License v3 (GPLv3)
*/
#include <fstream>
#include <gsl/gsl>
#include <aman/config/CommunicationFileFormat.h>
#include <aman/helper/String.h>
using namespace aman;
CommunicationFileFormat::CommunicationFileFormat() :
FileFormat() { }
bool CommunicationFileFormat::parse(const std::string& filename, Communication& config) {
config.valid = true;
std::ifstream stream(filename);
if (false == stream.is_open()) {
this->m_errorMessage = "Unable to open the configuration file: " + filename;
this->m_errorLine = 0;
config.valid = false;
return false;
}
std::string line;
std::uint32_t lineOffset = 0;
while (std::getline(stream, line)) {
std::string value;
lineOffset += 1;
/* skip a new line */
if (0 == line.length())
continue;
/* trimm the line and check if a comment line is used */
std::string trimmed = String::trim(line);
if (0 == trimmed.find_first_of('#', 0))
continue;
auto entry = String::splitString(trimmed, "=");
if (2 > entry.size()) {
this->m_errorLine = lineOffset;
this->m_errorMessage = "Invalid configuration entry";
config.valid = false;
return false;
}
else if (2 < entry.size()) {
for (std::size_t idx = 1; idx < entry.size() - 1; ++idx)
value += gsl::at(entry, idx) + "=";
value += entry.back();
}
else {
value = gsl::at(entry, 1);
}
/* found an invalid line */
if (0 == value.length()) {
this->m_errorLine = lineOffset;
this->m_errorMessage = "Invalid entry";
config.valid = false;
return false;
}
if ("URL" == gsl::at(entry, 0)) {
config.url = gsl::at(entry, 1);
}
else if ("Port" == gsl::at(entry, 0)) {
config.port = static_cast<std::uint16_t>(std::atoi(gsl::at(entry, 1).c_str()));
}
else if ("UID" == gsl::at(entry, 0)) {
config.identifier = gsl::at(entry, 1);
}
else {
this->m_errorLine = lineOffset;
this->m_errorMessage = "Unknown entry: " + gsl::at(entry, 0);
config.valid = false;
return false;
}
}
if (0 == lineOffset) {
this->m_errorLine = 0;
this->m_errorMessage = "No data found in " + filename;
config.valid = false;
return false;
}
return true;
}

37
src/config/FileFormat.cpp Normal file
View File

@@ -0,0 +1,37 @@
/*
* Author:
* Sven Czarnian <devel@svcz.de>
* Brief:
* Implements the generic file format
* Copyright:
* 2021 Sven Czarnian
* License:
* GNU General Public License v3 (GPLv3)
*/
#include <limits>
#include <aman/config/FileFormat.h>
using namespace aman;
FileFormat::FileFormat() noexcept :
m_errorLine(std::numeric_limits<std::uint32_t>::max()),
m_errorMessage() { }
void FileFormat::reset() noexcept {
this->m_errorLine = std::numeric_limits<std::uint32_t>::max();
this->m_errorMessage.clear();
}
bool FileFormat::errorFound() const noexcept {
return std::numeric_limits<std::uint32_t>::max() != this->m_errorLine;
}
std::uint32_t FileFormat::errorLine() const noexcept {
return this->m_errorLine;
}
const std::string& FileFormat::errorMessage() const noexcept {
return this->m_errorMessage;
}

View File

@@ -0,0 +1,94 @@
/*
* Author:
* Sven Czarnian <devel@svcz.de>
* Brief:
* Implements the identifier file format
* Copyright:
* 2021 Sven Czarnian
* License:
* GNU General Public License v3 (GPLv3)
*/
#include <fstream>
#include <gsl/gsl>
#include <aman/config/IdentifierFileFormat.h>
#include <aman/helper/String.h>
using namespace aman;
IdentifierFileFormat::IdentifierFileFormat() :
FileFormat() { }
bool IdentifierFileFormat::parse(const std::string& filename, Communication& config) {
config.valid = true;
std::ifstream stream(filename);
if (false == stream.is_open()) {
this->m_errorMessage = "Unable to open the configuration file: " + filename;
this->m_errorLine = 0;
config.valid = false;
return false;
}
std::string line;
std::uint32_t lineOffset = 0;
while (std::getline(stream, line)) {
std::string value;
lineOffset += 1;
/* skip a new line */
if (0 == line.length())
continue;
/* trimm the line and check if a comment line is used */
std::string trimmed = String::trim(line);
if (0 == trimmed.find_first_of('#', 0))
continue;
auto entry = String::splitString(trimmed, "=");
if (2 > entry.size()) {
this->m_errorLine = lineOffset;
this->m_errorMessage = "Invalid configuration entry";
config.valid = false;
return false;
}
else if (2 < entry.size()) {
for (std::size_t idx = 1; idx < entry.size() - 1; ++idx)
value += gsl::at(entry, idx) + "=";
value += entry.back();
}
else {
value = gsl::at(entry, 1);
}
/* found an invalid line */
if (0 == value.length()) {
this->m_errorLine = lineOffset;
this->m_errorMessage = "Invalid entry";
config.valid = false;
return false;
}
if ("UID" == gsl::at(entry, 0)) {
config.identifier = gsl::at(entry, 1);
}
else {
this->m_errorLine = lineOffset;
this->m_errorMessage = "Unknown entry: " + gsl::at(entry, 0);
config.valid = false;
return false;
}
}
if (0 == lineOffset) {
this->m_errorLine = 0;
this->m_errorMessage = "No data found in " + filename;
config.valid = false;
return false;
}
return true;
}