diff --git a/.gitignore b/.gitignore index b2ea1e0..00a0001 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ RDB_Diplomaterv_Monitor.pro.user* RDB_Monitor.pro.user* tests/test_config.json tests/protocol_emulator/emulators/__pycache__/* +.idea +cmake-build-debug + diff --git a/application/sources/backend.cpp b/application/sources/backend.cpp index 871136e..18c606b 100644 --- a/application/sources/backend.cpp +++ b/application/sources/backend.cpp @@ -35,8 +35,7 @@ Backend::Backend() : QObject(), gui_signal_interface(nullptr) available_connection_handlers.append(QString::fromStdString(serial_port_connection_name)); available_protocol_handlers.append(QString::fromStdString(measurement_data_protocol_name)); - // Deactivate the CMDP before merging as this Protocol is not fully implemented yet! - //available_protocol_handlers.append(QString::fromStdString(continous_measurement_data_protocol_name)); + available_protocol_handlers.append(QString::fromStdString(continous_measurement_data_protocol_name)); file_handlers.push_back(std::make_shared()); } @@ -105,22 +104,36 @@ void Backend::ReportStatus(const std::string& message) } } -void Backend::StoreNetworkDiagrams(const QString& connection_name, std::vector& new_diagrams) +std::vector Backend::StoreNetworkDiagrams(const QString& connection_name, std::vector& new_diagrams) { - StoreDiagrams(new_diagrams, + auto result = StoreDiagrams(new_diagrams, [&](const DefaultDiagram& diagram_to_add) -> QModelIndex { return diagram_container.AddDiagramFromNetwork(connection_name.toStdString(), diagram_to_add); }); + + return result; } -void Backend::StoreFileDiagrams(const std::string& file_name, const std::string& file_path, std::vector& new_diagrams) +std::vector Backend::StoreFileDiagrams(const std::string& file_name, const std::string& file_path, std::vector& new_diagrams) { - StoreDiagrams(new_diagrams, + auto result = StoreDiagrams(new_diagrams, [&](const DefaultDiagram& diagram_to_add) -> QModelIndex { return diagram_container.AddDiagramFromFile(file_name, file_path, diagram_to_add); }); + + return result; +} + +void Backend::UpdateNetworkDiagram(const QModelIndex& model_index, const DefaultDiagram& updated_diagram) +{ + auto diagram_ptr = diagram_container.GetDiagram(model_index); + if(diagram_ptr) + { + // TODO This should be optimized because now for every update (even if that is only just a new point) the whole diagram will be overwritten. See Issue #53 + *diagram_ptr = updated_diagram; + } } std::vector Backend::GetSupportedFileExtensions(void) @@ -165,10 +178,19 @@ void Backend::OpenNetworkConnection(const ConnectionRequestData& request_data) connection, connection_settings, protocol, - std::bind(&Backend::StoreNetworkDiagrams, this, std::placeholders::_1, std::placeholders::_2), + std::bind(&Backend::StoreNetworkDiagrams, this, unique_user_defined_name, std::placeholders::_1), + std::bind(&Backend::UpdateNetworkDiagram, this, std::placeholders::_1, std::placeholders::_2), std::bind(&Backend::ReportStatus, this, std::placeholders::_1)); - std::string status_message = "The connection \"" + unique_user_defined_name.toStdString() + "\" was successfully opened!"; + std::string status_message = "The connection \"" + unique_user_defined_name.toStdString() + "\" "; + if(network_handlers[unique_user_defined_name]->Run()) + { + status_message = + "was successfully opened!"; + } + else + { + status_message = "could not be opened! Please delete it andwas successfully opened!"; + } ReportStatus(status_message); auto active_connections = getActiveConnections(); @@ -207,10 +229,10 @@ void Backend::ImportFile(const std::string& path_to_file) bool the_file_was_processed = false; for(auto const& file_handler : file_handlers) { - if(file_handler->CanThisFileBeProcessed(path_to_file)) + if(file_handler->CanThisFileBeImportedFrom(path_to_file)) { std::ifstream file_stream(path_to_file); - auto diagrams_from_file = file_handler->ProcessData(file_stream); + auto diagrams_from_file = file_handler->ImportFromFile(file_stream); StoreFileDiagrams(file_name, path_to_file, diagrams_from_file); // Updating the configuration with the folder of the file that was imported @@ -258,7 +280,7 @@ void Backend::ExportFileStoreCheckedDiagrams(const std::string& path_to_file) auto checked_diagrams = diagram_container.GetCheckedDiagrams(); if(checked_diagrams.size()) { - auto exported_data = protocol_handler->ExportData(checked_diagrams); + auto exported_data = protocol_handler->ExportToFile(checked_diagrams); std::ofstream output_file_stream(path_to_file, (std::ofstream::out | std::ofstream::trunc)); output_file_stream << exported_data.rdbuf(); @@ -282,8 +304,9 @@ void Backend::ExportFileStoreCheckedDiagrams(const std::string& path_to_file) } } -void Backend::StoreDiagrams(std::vector& new_diagrams, const std::function storage_logic) +std::vector Backend::StoreDiagrams(std::vector& new_diagrams, const std::function storage_logic) { + std::vector result; auto container_is_empty = (0 == diagram_container.GetNumberOfDiagrams()); // Adding the diagrams to the diagram_container @@ -291,6 +314,7 @@ void Backend::StoreDiagrams(std::vector& new_diagrams, const std { // Calling the logic that does the storage for a single diagram, this is provided by the caller auto recently_added_diagram = storage_logic(i); + result.push_back(recently_added_diagram); // Displaying the diagram if this was the first if(container_is_empty) @@ -306,6 +330,8 @@ void Backend::StoreDiagrams(std::vector& new_diagrams, const std } ReportStatus(std::to_string(new_diagrams.size()) + " new diagram was added to the list."); + + return result; } QStringList Backend::getActiveConnections(void) diff --git a/application/sources/backend.hpp b/application/sources/backend.hpp index 3ac0407..5cc5039 100644 --- a/application/sources/backend.hpp +++ b/application/sources/backend.hpp @@ -61,8 +61,9 @@ class Backend : public QObject, public I_BackendSignal void ReportStatus(const std::string& message); - void StoreNetworkDiagrams(const QString& connection_name, std::vector& new_diagrams); - void StoreFileDiagrams(const std::string& file_name, const std::string& file_path, std::vector& new_diagrams); + std::vector StoreNetworkDiagrams(const QString& connection_name, std::vector& new_diagrams); + std::vector StoreFileDiagrams(const std::string& file_name, const std::string& file_path, std::vector& new_diagrams); + void UpdateNetworkDiagram(const QModelIndex& model_index, const DefaultDiagram& updated_diagram); QAbstractItemModel* GetDiagramContainerModel(void) override {return &diagram_container;} std::string GetFileImportDefaultFolder(void) override {return configuration.ImportFolder();} @@ -86,7 +87,7 @@ private slots: void ExportFileStoreCheckedDiagrams(const std::string& path_to_file); private: - void StoreDiagrams(std::vector& new_diagrams, const std::function storage_logic); + std::vector StoreDiagrams(std::vector& new_diagrams, const std::function storage_logic); QStringList getActiveConnections(void); QString makeUserDefinedConnectionNameUnique(const QString& user_defined_name); diff --git a/application/sources/continous_measurement_data_protocol.cpp b/application/sources/continous_measurement_data_protocol.cpp index 54435c6..8ffbee4 100644 --- a/application/sources/continous_measurement_data_protocol.cpp +++ b/application/sources/continous_measurement_data_protocol.cpp @@ -36,7 +36,7 @@ std::string ContinousMeasurementDataProtocol::GetProtocolName(void) return continous_measurement_data_protocol_name; } -std::vector ContinousMeasurementDataProtocol::ProcessData(std::istream& input_data) +void ContinousMeasurementDataProtocol::ProcessNetworkData(std::istream& input_data) { std::vector assembled_diagrams; std::string actual_line_std_string; @@ -56,6 +56,7 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i state = Constants::States::ProcessingHeaderDiagramTitle; } break; + case Constants::States::ProcessingHeaderDiagramTitle: state = Constants::States::ProcessingHeaderDataLines; // If a header message diagram title line was found @@ -78,8 +79,11 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i actual_diagram = DefaultDiagram(current_date_and_time_string); // Switching to the next state without a break --> a new line will NOT be fetched, because this line is the headline } + // In both cases the actual model index needs to be invalidated as we are starting a new diagram + actual_model_index = QModelIndex(); // The falltrough is not an error in this case, this behaviour needed because there was no diagram title found, the actual_line contains the headline - [[fallthrough]]; + + [[fallthrough]]; case Constants::States::ProcessingHeaderDataLines: match = regex_patterns.header_datalines.match(actual_line); if(match.hasMatch()) @@ -93,7 +97,7 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i QRegularExpressionMatchIterator regex_iterator = regex_patterns.header_dataline_y_titles.globalMatch(y_titles); while(regex_iterator.hasNext()) { - auto match = regex_iterator.next(); + match = regex_iterator.next(); auto y_id = match.captured("id"); auto y_title = match.captured("title"); @@ -103,7 +107,6 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i state = Constants::States::WaitingForHeaderMessageStart; } } - state = Constants::States::WaitingForHeaderMessageEnd; } else @@ -111,6 +114,7 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i state = Constants::States::WaitingForHeaderMessageStart; } break; + case Constants::States::WaitingForHeaderMessageEnd: // If a header message end line was found match = regex_patterns.header_end.match(actual_line); @@ -123,6 +127,7 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i state = Constants::States::WaitingForHeaderMessageStart; } break; + case Constants::States::WaitingForDataMessageStart: // If a header message end line was found match = regex_patterns.data_start.match(actual_line); @@ -137,6 +142,7 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i state = Constants::States::WaitingForHeaderMessageStart; } break; + case Constants::States::ProcessingDataMessageContent: match = regex_patterns.data_content.match(actual_line); if(match.hasMatch()) @@ -147,7 +153,7 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i QRegularExpressionMatchIterator regex_iterator = regex_patterns.data_y_content.globalMatch(y_values); while(regex_iterator.hasNext()) { - auto match = regex_iterator.next(); + match = regex_iterator.next(); auto y_id = match.captured("id"); auto y_value = match.captured("value"); @@ -175,29 +181,70 @@ std::vector ContinousMeasurementDataProtocol::ProcessData(std::i } } break; + default: state = Constants::States::WaitingForHeaderMessageStart; - throw("The DataProcessor::ProcessData's statemachine switched to an unexpected state: " + std::to_string(static_cast::type>(state))); + throw("The ContinousMeasurementDataProtocol::ProcessNetworkData's statemachine switched to an unexpected state: " + + std::to_string(static_cast::type>(state))); break; } } - return assembled_diagrams; + // If the actual model index is valid then this is an update and not a new diagram + if(actual_model_index.isValid()) + { + // We should not have assembled diagrams here, only an update to the actual diagram + if(!assembled_diagrams.empty()) + { + throw("The ContinousMeasurementDataProtocol::ProcessNetworkData detected an error: the actual_model_index was valid but the assembled_diagrams was not empty!"); + } + + // Sending the updated diagram + if(m_diagram_updater) + { + m_diagram_updater(actual_model_index, actual_diagram); + } + } + else + { + // If the model index is not valid then we are sending new diagrams + if(m_diagram_collector) + { + auto model_indexes = m_diagram_collector(assembled_diagrams); + + // If we have the waiting for header start state here then we either had a message error + // or a tail message was received. In both cases the last diagram is finished. This means that the + // model index does not need to be stored as no update will be sent anymore. + if(state != Constants::States::WaitingForHeaderMessageStart) + { + // We need to only store the last model index, + // because we are only going to be able to send updates to the last diagram. + actual_model_index = model_indexes.back(); + } + } + } } -std::stringstream ContinousMeasurementDataProtocol::ExportData(const std::vector& diagrams_to_export) +std::string ContinousMeasurementDataProtocol::GetSupportedFileType(void) { - (void) diagrams_to_export; - throw("The Continous Measurement Protocol does not support exporting into files!"); + // The CMDP protocol does not support any file types + return std::string(); } -bool ContinousMeasurementDataProtocol::CanThisFileBeProcessed(const std::string path_to_file) +bool ContinousMeasurementDataProtocol::CanThisFileBeImportedFrom(const std::string path_to_file) { (void) path_to_file; // The CMDP protocol does not support file processing return false; } +std::vector ContinousMeasurementDataProtocol::ImportFromFile(std::ifstream& file_stream) +{ + (void) file_stream; + // The CMDP protocol does not support file processing + return std::vector(); +} + bool ContinousMeasurementDataProtocol::CanThisFileBeExportedInto(const std::string path_to_file) { (void) path_to_file; @@ -205,8 +252,8 @@ bool ContinousMeasurementDataProtocol::CanThisFileBeExportedInto(const std::stri return false; } -std::string ContinousMeasurementDataProtocol::GetSupportedFileType(void) +std::stringstream ContinousMeasurementDataProtocol::ExportToFile(const std::vector& diagrams_to_export) { - // The CMDP protocol does not support any file types - return std::string(); + (void) diagrams_to_export; + throw("The Continous Measurement Protocol does not support exporting into files!"); } diff --git a/application/sources/continous_measurement_data_protocol.hpp b/application/sources/continous_measurement_data_protocol.hpp index 54e7d0d..7886b94 100644 --- a/application/sources/continous_measurement_data_protocol.hpp +++ b/application/sources/continous_measurement_data_protocol.hpp @@ -55,11 +55,12 @@ class ContinousMeasurementDataProtocol : public I_Protocol ContinousMeasurementDataProtocol& operator=(ContinousMeasurementDataProtocol&&) = delete; virtual std::string GetProtocolName(void) override; - virtual std::vector ProcessData(std::istream& input_data) override; - virtual std::stringstream ExportData(const std::vector& diagrams_to_export) override; - virtual bool CanThisFileBeProcessed(const std::string path_to_file) override; - virtual bool CanThisFileBeExportedInto(const std::string path_to_file) override; + virtual void ProcessNetworkData(std::istream& input_data) override; virtual std::string GetSupportedFileType(void) override; + virtual bool CanThisFileBeImportedFrom(const std::string path_to_file) override; + virtual std::vector ImportFromFile(std::ifstream& file_tream) override; + virtual std::stringstream ExportToFile(const std::vector& diagrams_to_export) override; + virtual bool CanThisFileBeExportedInto(const std::string path_to_file) override; private: struct Constants @@ -94,6 +95,7 @@ class ContinousMeasurementDataProtocol : public I_Protocol Constants::States state; DefaultDiagram actual_diagram; + QModelIndex actual_model_index; RegexPatterns regex_patterns; }; diff --git a/application/sources/i_connection.hpp b/application/sources/i_connection.hpp index 9419593..22e35b5 100644 --- a/application/sources/i_connection.hpp +++ b/application/sources/i_connection.hpp @@ -26,8 +26,7 @@ #include #include #include - -#include +#include class I_ConnectionSettings; @@ -35,19 +34,27 @@ class I_ConnectionSettings; class I_Connection { public: + using data_collector_t = std::function; + using error_reporter_t = std::function; + virtual ~I_Connection() = default; - virtual std::string getName(void) = 0; + virtual void RegisterCallbacks(const data_collector_t& data_collector, + const error_reporter_t& error_reporter) + { + m_data_collector = data_collector; + m_error_reporter = error_reporter; + } + + virtual std::string GetName(void) = 0; virtual bool Open(const std::shared_ptr settings) = 0; virtual void Close(void) = 0; virtual bool IsOpen(void) = 0; -signals: - virtual void DataReceived(std::istream& received_data) = 0; - virtual void ErrorReport(const std::string& error_message) = 0; +protected: + data_collector_t m_data_collector; + error_reporter_t m_error_reporter; }; -Q_DECLARE_INTERFACE(I_Connection, "ConnectionInterface") - #endif // I_CONNECTION_HPP diff --git a/application/sources/i_protocol.hpp b/application/sources/i_protocol.hpp index b01a82e..31a0802 100644 --- a/application/sources/i_protocol.hpp +++ b/application/sources/i_protocol.hpp @@ -25,6 +25,7 @@ #include #include +#include #include "diagram.hpp" @@ -32,15 +33,50 @@ class I_Protocol { public: + // Callback types + // Called when a network diagram got processed + using diagram_collector_t = std::function(std::vector&)>; + // Called when an update is available for a diagram + using diagram_updater_t = std::function; + // Called when an error was detected + using error_reporter_t = std::function; + virtual ~I_Protocol() = default; + virtual void RegisterCallbacks(const diagram_collector_t& diagram_collector, + const diagram_updater_t& diagram_updater, + const error_reporter_t& error_reporter) + { + m_diagram_collector = diagram_collector; + m_diagram_updater = diagram_updater; + m_error_reporter = error_reporter; + } + + // Returns the name of the protocol virtual std::string GetProtocolName(void) = 0; - virtual std::vector ProcessData(std::istream& input_data) = 0; - virtual std::stringstream ExportData(const std::vector& diagrams_to_export) = 0; - virtual bool CanThisFileBeProcessed(const std::string path_to_file) = 0; - virtual bool CanThisFileBeExportedInto(const std::string path_to_file) = 0; + + // Processes the data received from a network connection and reports the findings trough the callbacks + virtual void ProcessNetworkData(std::istream& input_data) = 0; + + // Returns the file extensions that can be imported virtual std::string GetSupportedFileType(void) = 0; -}; + // Checks whether the file can be imported + virtual bool CanThisFileBeImportedFrom(const std::string path_to_file) = 0; + + // Processes the content of the file and returns the found diagrams + virtual std::vector ImportFromFile(std::istream& input_stream) = 0; + + // Checks whether data can be exported into the file + virtual bool CanThisFileBeExportedInto(const std::string path_to_file) = 0; + + // Processes the received diagrams and returns a stream that can be written to a file + virtual std::stringstream ExportToFile(const std::vector& diagrams_to_export) = 0; + +protected: + diagram_collector_t m_diagram_collector; + diagram_updater_t m_diagram_updater; + error_reporter_t m_error_reporter; +}; #endif // I_PROTOCOL_HPP diff --git a/application/sources/measurement_data_protocol.cpp b/application/sources/measurement_data_protocol.cpp index d458df9..e7c7e7d 100644 --- a/application/sources/measurement_data_protocol.cpp +++ b/application/sources/measurement_data_protocol.cpp @@ -25,17 +25,94 @@ const std::string measurement_data_protocol_name = "Measurement Data Protocol - MDP"; -MeasurementDataProtocol::MeasurementDataProtocol() +std::string MeasurementDataProtocol::GetProtocolName(void) { - state = Constants::States::WaitingForStartLine; + return measurement_data_protocol_name; } -std::string MeasurementDataProtocol::GetProtocolName(void) +void MeasurementDataProtocol::ProcessNetworkData(std::istream& input_data) { - return measurement_data_protocol_name; + auto processed_diagrams = ProcessData(input_data, network_processing_data); + + if(m_diagram_collector) + { + m_diagram_collector(processed_diagrams); + } +} + +bool MeasurementDataProtocol::CanThisFileBeImportedFrom(const std::string path_to_file) +{ + bool bResult = false; + + std::string file_extension = QFileInfo(QString::fromStdString(path_to_file)).completeSuffix().toStdString(); + + if(std::string(Constants::native_file_extension) == file_extension) + { + bResult = true; + } + + return bResult; +} + +std::vector MeasurementDataProtocol::ImportFromFile(std::istream& input_stream) +{ + ProcessingData intermediate_data; + return ProcessData(input_stream, intermediate_data); +} + +bool MeasurementDataProtocol::CanThisFileBeExportedInto(const std::string path_to_file) +{ + bool bResult = false; + + std::string file_extension = QFileInfo(QString::fromStdString(path_to_file)).completeSuffix().toStdString(); + + if(std::string(Constants::native_file_extension) == file_extension) + { + bResult = true; + } + + return bResult; +} + +std::stringstream MeasurementDataProtocol::ExportToFile(const std::vector &diagrams_to_export) +{ + std::stringstream exported_data; + + for(auto const& diagram : diagrams_to_export) + { + exported_data << Constants::Export::start_line << std::endl; + + exported_data << Constants::Export::diagram_title_start << diagram.GetTitle() << Constants::Export::diagram_title_end << std::endl; + + auto number_of_data_lines = diagram.GetTheNumberOfDataLines(); + if(0 < number_of_data_lines) + { + exported_data << diagram.GetAxisXTitle() << Constants::Export::element_separator; + for(std::size_t data_line_index = 0; data_line_index < number_of_data_lines; data_line_index++) + { + exported_data << diagram.GetDataLineTitle(data_line_index) << Constants::Export::element_separator; + } + exported_data << std::endl; + + auto number_of_data_points = diagram.GetTheNumberOfDataPoints(0); + for(std::size_t data_point_index = 0; data_point_index < number_of_data_points; data_point_index++) + { + exported_data << diagram.GetDataPoint(0, data_point_index).GetX() << Constants::Export::element_separator; + for(std::size_t data_line_index = 0; data_line_index < number_of_data_lines; data_line_index++) + { + exported_data << diagram.GetDataPoint(data_line_index, data_point_index).GetY() << Constants::Export::element_separator; + } + exported_data << std::endl; + } + } + + exported_data << Constants::Export::end_line << std::endl << std::endl; + } + + return exported_data; } -std::vector MeasurementDataProtocol::ProcessData(std::istream& input_data) +std::vector MeasurementDataProtocol::ProcessData(std::istream& input_data, ProcessingData& processing_data) { std::vector assembled_diagrams; std::string actual_line; @@ -46,23 +123,23 @@ std::vector MeasurementDataProtocol::ProcessData(std::istream& i try { - switch(state) + switch(processing_data.state) { case Constants::States::WaitingForStartLine: // If a start line was found... if(std::regex_match(actual_line, std::regex(Constants::Regex::start_line))) { - state = Constants::States::ProcessingTitleLine; + processing_data.state = Constants::States::ProcessingTitleLine; } break; case Constants::States::ProcessingTitleLine: // In any case, we will switch to the next state - state = Constants::States::ProcessingHeadline; + processing_data.state = Constants::States::ProcessingHeadline; // If this is a diagram title line if(std::regex_search(actual_line, match_results, std::regex(Constants::Regex::title_line))) { // Then we create a diagram object with the title - actual_diagram = DefaultDiagram(match_results[1]); + processing_data.actual_diagram = DefaultDiagram(match_results[1]); // Switching to the next state with a break --> a new line will be fetched break; } @@ -73,12 +150,12 @@ std::vector MeasurementDataProtocol::ProcessData(std::istream& i std::string current_date_and_time_string = ctime(¤t_date_and_time); // The ctime adds an extra newline to the string, this needs to be removed current_date_and_time_string.pop_back(); - actual_diagram = DefaultDiagram(current_date_and_time_string); + processing_data.actual_diagram = DefaultDiagram(current_date_and_time_string); // Switching to the next state without a break --> a new line will NOT be fetched, because this line is the headline } - // The falltrough is not an error in this case, this behaviour needed because there was no diagram title found, the actual_line contains the headline - [[fallthrough]]; + // The falltrough is not an error in this case, this behaviour needed because there was no diagram title found, the actual_line contains the headline + [[fallthrough]]; case Constants::States::ProcessingHeadline: // If this is a headline but not a dataline // (this is needed because with regex it is difficult to define the differences between the data and headlines) @@ -93,23 +170,23 @@ std::vector MeasurementDataProtocol::ProcessData(std::istream& i { if(0 == column_index) { - actual_diagram.SetAxisXTitle(match_results[1]); + processing_data.actual_diagram.SetAxisXTitle(match_results[1]); } else { std::string dataline_id = "Y" + std::to_string(column_index - 1); - actual_diagram.AddNewDataLine(dataline_id, match_results[1]); + processing_data.actual_diagram.AddNewDataLine(dataline_id, match_results[1]); } ++column_index; headline = match_results.suffix().str(); } - state = Constants::States::ProcessingDataLines; + processing_data.state = Constants::States::ProcessingDataLines; } else { - state = Constants::States::WaitingForStartLine; + processing_data.state = Constants::States::WaitingForStartLine; } break; case Constants::States::ProcessingDataLines: @@ -129,16 +206,17 @@ std::vector MeasurementDataProtocol::ProcessData(std::istream& i } else { - if((column_index - 1) < actual_diagram.GetTheNumberOfDataLines()) + if((column_index - 1) < processing_data.actual_diagram.GetTheNumberOfDataLines()) { std::stringstream stringstream(match_results[1]); DefaultDiagram::coordinate_t data_point_y_value; stringstream >> data_point_y_value; - actual_diagram.AddNewDataPoint((column_index - 1), DefaultDiagram::DataPoint_t(data_point_x_value, data_point_y_value)); + processing_data.actual_diagram.AddNewDataPoint((column_index - 1), + DefaultDiagram::DataPoint_t(data_point_x_value, data_point_y_value)); } else { - state = Constants::States::WaitingForStartLine; + processing_data.state = Constants::States::WaitingForStartLine; break; } } @@ -146,23 +224,24 @@ std::vector MeasurementDataProtocol::ProcessData(std::istream& i ++column_index; data_line = match_results.suffix().str(); } - if((column_index - 1) != actual_diagram.GetTheNumberOfDataLines()) + if((column_index - 1) != processing_data.actual_diagram.GetTheNumberOfDataLines()) { - state = Constants::States::WaitingForStartLine; + processing_data.state = Constants::States::WaitingForStartLine; } } else { if(std::regex_match(actual_line, std::regex(Constants::Regex::end_line))) { - assembled_diagrams.push_back(actual_diagram); + assembled_diagrams.push_back(processing_data.actual_diagram); } - state = Constants::States::WaitingForStartLine; + processing_data.state = Constants::States::WaitingForStartLine; } break; default: - state = Constants::States::WaitingForStartLine; - throw("The DataProcessor::ProcessData's statemachine switched to an unexpected state: " + std::to_string(static_cast::type>(state))); + processing_data.state = Constants::States::WaitingForStartLine; + throw("The DataProcessor::ProcessData's statemachine switched to an unexpected state: " + + std::to_string(static_cast::type>(processing_data.state))); break; } } @@ -174,69 +253,3 @@ std::vector MeasurementDataProtocol::ProcessData(std::istream& i return assembled_diagrams; } - -bool MeasurementDataProtocol::CanThisFileBeProcessed(const std::string path_to_file) -{ - bool bResult = false; - - std::string file_extension = QFileInfo(QString::fromStdString(path_to_file)).completeSuffix().toStdString(); - - if(std::string(Constants::native_file_extension) == file_extension) - { - bResult = true; - } - - return bResult; -} - -std::stringstream MeasurementDataProtocol::ExportData(const std::vector& diagrams_to_export) -{ - std::stringstream exported_data; - - for(auto const& diagram : diagrams_to_export) - { - exported_data << Constants::Export::start_line << std::endl; - - exported_data << Constants::Export::diagram_title_start << diagram.GetTitle() << Constants::Export::diagram_title_end << std::endl; - - auto number_of_data_lines = diagram.GetTheNumberOfDataLines(); - if(0 < number_of_data_lines) - { - exported_data << diagram.GetAxisXTitle() << Constants::Export::element_separator; - for(std::size_t data_line_index = 0; data_line_index < number_of_data_lines; data_line_index++) - { - exported_data << diagram.GetDataLineTitle(data_line_index) << Constants::Export::element_separator; - } - exported_data << std::endl; - - auto number_of_data_points = diagram.GetTheNumberOfDataPoints(0); - for(std::size_t data_point_index = 0; data_point_index < number_of_data_points; data_point_index++) - { - exported_data << diagram.GetDataPoint(0, data_point_index).GetX() << Constants::Export::element_separator; - for(std::size_t data_line_index = 0; data_line_index < number_of_data_lines; data_line_index++) - { - exported_data << diagram.GetDataPoint(data_line_index, data_point_index).GetY() << Constants::Export::element_separator; - } - exported_data << std::endl; - } - } - - exported_data << Constants::Export::end_line << std::endl << std::endl; - } - - return exported_data; -} - -bool MeasurementDataProtocol::CanThisFileBeExportedInto(const std::string path_to_file) -{ - bool bResult = false; - - std::string file_extension = QFileInfo(QString::fromStdString(path_to_file)).completeSuffix().toStdString(); - - if(std::string(Constants::native_file_extension) == file_extension) - { - bResult = true; - } - - return bResult; -} diff --git a/application/sources/measurement_data_protocol.hpp b/application/sources/measurement_data_protocol.hpp index 76a7cd7..e6b0ccd 100644 --- a/application/sources/measurement_data_protocol.hpp +++ b/application/sources/measurement_data_protocol.hpp @@ -23,9 +23,11 @@ #define MEAUREMENT_DATA_PROTOCOL_HPP -#include +#include #include #include +#include +#include #include #include #include @@ -45,7 +47,7 @@ extern const std::string measurement_data_protocol_name; class MeasurementDataProtocol : public I_Protocol { public: - MeasurementDataProtocol(); + MeasurementDataProtocol() = default; virtual ~MeasurementDataProtocol() = default; MeasurementDataProtocol(const MeasurementDataProtocol&) = delete; @@ -55,11 +57,12 @@ class MeasurementDataProtocol : public I_Protocol MeasurementDataProtocol& operator=(MeasurementDataProtocol&&) = delete; virtual std::string GetProtocolName(void) override; - virtual std::vector ProcessData(std::istream& input_data) override; - virtual bool CanThisFileBeProcessed(const std::string path_to_file) override; + virtual void ProcessNetworkData(std::istream& input_data) override; virtual std::string GetSupportedFileType(void) override { return Constants::native_file_extension; } - virtual std::stringstream ExportData(const std::vector& diagrams_to_export) override; + virtual bool CanThisFileBeImportedFrom(const std::string path_to_file) override; + virtual std::vector ImportFromFile(std::istream& input_stream) override; virtual bool CanThisFileBeExportedInto(const std::string path_to_file) override; + virtual std::stringstream ExportToFile(const std::vector& diagrams_to_export) override; private: struct Constants @@ -97,8 +100,14 @@ class MeasurementDataProtocol : public I_Protocol }; }; - Constants::States state; - DefaultDiagram actual_diagram; + struct ProcessingData + { + Constants::States state = Constants::States::WaitingForStartLine; + DefaultDiagram actual_diagram; + }; + + std::vector ProcessData(std::istream& input_data, ProcessingData& processing_data); + ProcessingData network_processing_data; }; diff --git a/application/sources/network_handler.cpp b/application/sources/network_handler.cpp index d9b88ad..b4c8c22 100644 --- a/application/sources/network_handler.cpp +++ b/application/sources/network_handler.cpp @@ -19,19 +19,41 @@ //==============================================================================// +#include + #include "network_handler.hpp" #include "i_connection.hpp" #include "i_protocol.hpp" +NetworkHandler::NetworkHandler(const QString &user_defined_name, + std::shared_ptr connection_interface, + std::shared_ptr connection_settings, + std::shared_ptr protocol_interface, + I_Protocol::diagram_collector_t diagram_collector, + I_Protocol::diagram_updater_t diagram_updater, + error_collector_t error_collector) : + m_user_defined_name(user_defined_name), + m_connection_interface(connection_interface), + m_connection_settings(connection_settings), + m_protocol_interface(protocol_interface), + m_diagram_collector(diagram_collector), + m_diagram_updater(diagram_updater), + m_error_collector(error_collector) +{ + m_connection_interface->RegisterCallbacks(std::bind(&NetworkHandler::DataAvailable, this, std::placeholders::_1), + m_error_collector); + m_protocol_interface->RegisterCallbacks(m_diagram_collector, + m_diagram_updater, + m_error_collector); +} + bool NetworkHandler::Run(void) { bool result = false; - if(connection_interface->Open(connection_settings)) + if(m_connection_interface->Open(m_connection_settings)) { - QObject::connect(dynamic_cast(connection_interface.get()), SIGNAL(DataReceived(std::istream&)), this, SLOT(DataAvailable(std::istream&))); - QObject::connect(dynamic_cast(connection_interface.get()), SIGNAL(ErrorReport(const std::string&)), this, SLOT(ErrorReport(const std::string&))); result = true; } @@ -40,22 +62,10 @@ bool NetworkHandler::Run(void) void NetworkHandler::Stop(void) { - connection_interface->Close(); - QObject::disconnect(dynamic_cast(connection_interface.get()), SIGNAL(DataReceived(std::istream&)), this, SLOT(DataAvailable(std::istream&))); - QObject::disconnect(dynamic_cast(connection_interface.get()), SIGNAL(ErrorReport(const std::string&)), this, SLOT(ErrorReport(const std::string&))); + m_connection_interface->Close(); } void NetworkHandler::DataAvailable(std::istream& received_data) { - auto assembled_diagrams = protocol_interface->ProcessData(received_data); - - if(!assembled_diagrams.empty()) - { - diagram_collector(user_defined_name, assembled_diagrams); - } -} - -void NetworkHandler::ErrorReport(const std::string& error_message) -{ - error_collector(error_message); + m_protocol_interface->ProcessNetworkData(received_data); } diff --git a/application/sources/network_handler.hpp b/application/sources/network_handler.hpp index 18a0b04..0d6b30e 100644 --- a/application/sources/network_handler.hpp +++ b/application/sources/network_handler.hpp @@ -25,66 +25,31 @@ #include #include -#include #include #include +#include #include "diagram.hpp" - +#include "i_protocol.hpp" class I_Connection; class I_ConnectionSettings; -class I_Protocol; class NetworkHandler : public QObject { Q_OBJECT public: + using error_collector_t = std::function; - using diagram_collector_type = std::function&)>; - using error_collector_type = std::function; - - NetworkHandler(const QString& new_user_defined_name, - std::shared_ptr new_connection_interface, - std::shared_ptr new_connection_settings, - std::shared_ptr new_protocol_interface, - diagram_collector_type new_diagram_collector, - error_collector_type new_error_collector) - : user_defined_name(new_user_defined_name), - connection_interface(new_connection_interface), - connection_settings(new_connection_settings), - protocol_interface(new_protocol_interface), - diagram_collector(new_diagram_collector), - error_collector(new_error_collector) - { - if(!connection_interface) - { - std::string errorMessage = "There was no connection_interface set in NetworkHandler::NetworkHandler!"; - throw errorMessage; - } - if(!connection_settings) - { - std::string errorMessage = "There was no connection_settings set in NetworkHandler::NetworkHandler!"; - throw errorMessage; - } - if(!protocol_interface) - { - std::string errorMessage = "There was no protocol_interface set in NetworkHandler::NetworkHandler!"; - throw errorMessage; - } - if(!diagram_collector) - { - std::string errorMessage = "There was no diagram_collector set in NetworkHandler::NetworkHandler!"; - throw errorMessage; - } - if(!error_collector) - { - std::string errorMessage = "There was no error_collector set in NetworkHandler::NetworkHandler!"; - throw errorMessage; - } - } + NetworkHandler(const QString& user_defined_name, + std::shared_ptr connection_interface, + std::shared_ptr connection_settings, + std::shared_ptr protocol_interface, + I_Protocol::diagram_collector_t diagram_collector, + I_Protocol::diagram_updater_t diagram_updater, + error_collector_t error_collector); ~NetworkHandler() = default; @@ -96,19 +61,18 @@ class NetworkHandler : public QObject bool Run(void); void Stop(void); - QString getUserDefinedName(void) { return user_defined_name; } + QString GetUserDefinedName(void) { return m_user_defined_name; } -private slots: +private: void DataAvailable(std::istream& received_data); - void ErrorReport(const std::string& error_message); -private: - QString user_defined_name; - std::shared_ptr connection_interface; - std::shared_ptr connection_settings; - std::shared_ptr protocol_interface; - diagram_collector_type diagram_collector; - error_collector_type error_collector; + QString m_user_defined_name; + std::shared_ptr m_connection_interface; + std::shared_ptr m_connection_settings; + std::shared_ptr m_protocol_interface; + I_Protocol::diagram_collector_t m_diagram_collector; + I_Protocol::diagram_updater_t m_diagram_updater; + error_collector_t m_error_collector; }; diff --git a/application/sources/serial_port.cpp b/application/sources/serial_port.cpp index 4c9e925..e9f2097 100644 --- a/application/sources/serial_port.cpp +++ b/application/sources/serial_port.cpp @@ -23,9 +23,18 @@ #include "serial_port_settings.hpp" +SerialPort::~SerialPort() +{ + if(port) + { + port->close(); + port.reset(); + } +} + extern const std::string serial_port_connection_name = "SerialPort"; -std::string SerialPort::getName(void) +std::string SerialPort::GetName(void) { return serial_port_connection_name; } @@ -39,7 +48,6 @@ bool SerialPort::Open(const std::shared_ptr settings) { try { - std::shared_ptr serial_port_settings = std::dynamic_pointer_cast(settings); port = std::make_unique(); port->setPortName(serial_port_settings->portName); port->setBaudRate(serial_port_settings->baudRate); @@ -50,8 +58,8 @@ bool SerialPort::Open(const std::shared_ptr settings) if(port->open(QIODevice::ReadOnly)) { - QObject::connect(port.get(), &QSerialPort::readyRead, this, &SerialPort::ReadLineFromPort); - QObject::connect(port.get(), &QSerialPort::errorOccurred, this, &SerialPort::HandleErrors); + QObject::connect(port.get(), &QSerialPort::readyRead, this, &SerialPort::ReadLineFromPort); + QObject::connect(port.get(), &QSerialPort::errorOccurred, this, &SerialPort::HandleErrors); result = true; } else @@ -107,7 +115,10 @@ void SerialPort::ReadLineFromPort(void) if(at_least_one_line_was_received) { std::stringstream received_data_stream(received_lines); - emit DataReceived(received_data_stream); + if(m_data_collector) + { + m_data_collector(received_data_stream); + } } } @@ -115,6 +126,14 @@ void SerialPort::HandleErrors(QSerialPort::SerialPortError error) { if(QSerialPort::ReadError == error) { - emit ErrorReport((QObject::tr("SerialPort::HandleErrors: An I/O error occurred while reading the data from port %1, error: %2").arg(port->portName()).arg(port->errorString())).toStdString()); + std::string error_message = "SerialPort::HandleErrors: An I/O error occurred while reading the data from port " + + port->portName().toStdString() + + ", error: " + + port->errorString().toStdString(); + + if(m_error_reporter) + { + m_error_reporter(error_message); + } } } diff --git a/application/sources/serial_port.hpp b/application/sources/serial_port.hpp index 8cea38d..6665cd4 100644 --- a/application/sources/serial_port.hpp +++ b/application/sources/serial_port.hpp @@ -39,35 +39,22 @@ extern const std::string serial_port_connection_name; class SerialPort : public QObject, public I_Connection { Q_OBJECT - Q_INTERFACES(I_Connection) public: SerialPort() = default; - - ~SerialPort() - { - if(port) - { - port->close(); - port.reset(); - } - } - SerialPort(const SerialPort&) = delete; SerialPort(SerialPort&&) = delete; + ~SerialPort(); + SerialPort& operator=(const SerialPort&) = delete; SerialPort& operator=(SerialPort&&) = delete; - std::string getName(void) override; + std::string GetName(void) override; bool Open(const std::shared_ptr settings) override; void Close(void) override; bool IsOpen(void) override; -signals: - void DataReceived(std::istream& received_data) override; - void ErrorReport(const std::string& error_message) override; - private slots: void ReadLineFromPort(void); void HandleErrors(QSerialPort::SerialPortError error); diff --git a/tests/sources/test_measurement_data_protocol.cpp b/tests/sources/test_measurement_data_protocol.cpp index 6f98a20..cc14a8d 100644 --- a/tests/sources/test_measurement_data_protocol.cpp +++ b/tests/sources/test_measurement_data_protocol.cpp @@ -19,18 +19,34 @@ //==============================================================================// -#include -#include +#include #include "../application/sources/measurement_data_protocol.hpp" #include "test_protocol_common.h" +using testing::_; + class TestMeasurementDataProtocol : public ::testing::Test, public testing::WithParamInterface { protected: + TestMeasurementDataProtocol() + { + test_mdp_processor.RegisterCallbacks(std::bind(&ProtocolCallbackMocks::diagram_collector, &mocks, std::placeholders::_1), + std::bind(&ProtocolCallbackMocks::diagram_updater, &mocks, std::placeholders::_1, std::placeholders::_2), + std::bind(&ProtocolCallbackMocks::error_reporter, &mocks, std::placeholders::_1)); + } + + void ExpectNoCallbacks(void) + { + EXPECT_CALL(mocks, diagram_collector(_)).Times(0); + EXPECT_CALL(mocks, diagram_updater(_, _)).Times(0); + EXPECT_CALL(mocks, error_reporter(_)).Times(0); + } + MeasurementDataProtocol test_mdp_processor; + ProtocolCallbackMocks mocks; std::string expected_protocol_name = measurement_data_protocol_name; std::string expected_file_type = "mdp"; std::vector processed_diagrams; @@ -38,42 +54,68 @@ class TestMeasurementDataProtocol : public ::testing::Test, TEST_F(TestMeasurementDataProtocol, GetProtocolName) { + ExpectNoCallbacks(); + EXPECT_EQ(test_mdp_processor.GetProtocolName(), expected_protocol_name); } -TEST_F(TestMeasurementDataProtocol, CanThisFileBeProcessed) +TEST_P(TestMeasurementDataProtocol, ProcessNetworkData) { - std::string filename_to_test = std::string("myfile.") + expected_file_type; - EXPECT_TRUE(test_mdp_processor.CanThisFileBeProcessed(filename_to_test)); - filename_to_test = std::string("mymdpfile.") + std::string("txt"); - EXPECT_FALSE(test_mdp_processor.CanThisFileBeProcessed(filename_to_test)); + auto test_parameter = GetParam(); + std::ifstream file_stream = TestFileReader::read(test_parameter.file_name); + + EXPECT_CALL(mocks, diagram_collector(testing::SizeIs(test_parameter.expected_correct_diagrams))) + .Times(1); + ON_CALL(mocks, diagram_collector) + .WillByDefault(testing::Return(std::vector(test_parameter.expected_correct_diagrams))); + EXPECT_CALL(mocks, diagram_updater(_, _)).Times(0); + EXPECT_CALL(mocks, error_reporter(_)).Times(0); + + test_mdp_processor.ProcessNetworkData(file_stream); } TEST_F(TestMeasurementDataProtocol, GetSupportedFileType) { + ExpectNoCallbacks(); + EXPECT_EQ(test_mdp_processor.GetSupportedFileType(), expected_file_type); } -TEST_F(TestMeasurementDataProtocol, ProcessData_ExportData_EmptyStream) +TEST_F(TestMeasurementDataProtocol, CanThisFileBeImportedFrom) +{ + ExpectNoCallbacks(); + + std::string filename_to_test = std::string("myfile.") + expected_file_type; + EXPECT_TRUE(test_mdp_processor.CanThisFileBeImportedFrom(filename_to_test)); + + filename_to_test = std::string("mymdpfile.") + std::string("txt"); + EXPECT_FALSE(test_mdp_processor.CanThisFileBeImportedFrom(filename_to_test)); +} + +TEST_F(TestMeasurementDataProtocol, ImportFromFile_ExportToFile_EmptyStream) { + ExpectNoCallbacks(); + std::ifstream empty_stream; - processed_diagrams = test_mdp_processor.ProcessData(empty_stream); + processed_diagrams = test_mdp_processor.ImportFromFile(empty_stream); EXPECT_EQ(processed_diagrams.size(), std::size_t(0)); - std::stringstream exported_data = test_mdp_processor.ExportData(processed_diagrams); - processed_diagrams = test_mdp_processor.ProcessData(exported_data); + std::stringstream exported_data = test_mdp_processor.ExportToFile(processed_diagrams); + processed_diagrams = test_mdp_processor.ImportFromFile(exported_data); EXPECT_EQ(processed_diagrams.size(), std::size_t(0)); } -TEST_P(TestMeasurementDataProtocol, ProcessData_ExportData) +TEST_P(TestMeasurementDataProtocol, ImportFromFile_ExportToFile) { + ExpectNoCallbacks(); + auto test_parameter = GetParam(); std::ifstream file_stream = TestFileReader::read(test_parameter.file_name); - processed_diagrams = test_mdp_processor.ProcessData(file_stream); + processed_diagrams = test_mdp_processor.ImportFromFile(file_stream); EXPECT_EQ(processed_diagrams.size(), std::size_t(test_parameter.expected_correct_diagrams)) << "test_file: " << test_parameter.file_name.toStdString(); - std::stringstream exported_data = test_mdp_processor.ExportData(processed_diagrams); - processed_diagrams = test_mdp_processor.ProcessData(exported_data); + std::stringstream exported_data = test_mdp_processor.ExportToFile(processed_diagrams); + processed_diagrams = test_mdp_processor.ImportFromFile(exported_data); EXPECT_EQ(processed_diagrams.size(), std::size_t(test_parameter.expected_correct_diagrams)) << "test_file: " << test_parameter.file_name.toStdString(); } diff --git a/tests/sources/test_protocol_common.h b/tests/sources/test_protocol_common.h index de7b7a6..95c9d29 100644 --- a/tests/sources/test_protocol_common.h +++ b/tests/sources/test_protocol_common.h @@ -24,15 +24,33 @@ #include +#include +#include #include +#include #include #include #include +#include #include +/*struct ProtocolCallbackMocks { + testing::MockFunction(std::vector &)> diagram_collector; + testing::MockFunction diagram_updater; + testing::MockFunction error_reporter; +};*/ + +class ProtocolCallbackMocks +{ +public: + MOCK_METHOD(std::vector, diagram_collector, (std::vector&), ()); + MOCK_METHOD(void, diagram_updater, (const QModelIndex&, const DefaultDiagram&), ()); + MOCK_METHOD(void, error_reporter, (const std::string&), ()); +}; + struct TestProtocolParameter { TestProtocolParameter(const QString& new_file_name, @@ -58,4 +76,5 @@ struct TestFileReader } }; + #endif // TEST_PROTOCOL_COMMON_H