From 70ff5e8cf1717f0878e079235a1b427b46b08e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= <d_muel20@uni-muenster.de> Date: Fri, 30 Nov 2018 14:37:59 +0100 Subject: [PATCH] * Added first version of Barista Training Task --- Grinder/Grinder.pro | 20 +- Grinder/Version.h | 6 +- .../serialization/JsonSettingsCodec.cpp | 2 +- .../serialization/SettingsContainer.cpp | 19 +- .../common/serialization/SettingsContainer.h | 4 +- Grinder/task/Task.h | 2 +- Grinder/task/TaskCatalog.cpp | 2 + Grinder/task/TaskType.cpp | 2 + Grinder/task/TaskType.h | 2 + Grinder/task/tasks/BaristaMessage.cpp | 53 +++ Grinder/task/tasks/BaristaMessage.h | 45 +++ Grinder/task/tasks/BaristaProtocol.h | 15 + Grinder/task/tasks/BaristaTask.cpp | 312 ++++++++++++++++++ Grinder/task/tasks/BaristaTask.h | 109 ++++++ Grinder/task/tasks/BaristaTrainingTask.cpp | 149 +++++++++ Grinder/task/tasks/BaristaTrainingTask.h | 68 ++++ Grinder/task/tasks/GenericTask.cpp | 4 +- Grinder/ui/dlg/TextViewerDialog.cpp | 1 + .../ui/mainwnd/PropertyTreeItemDelegate.cpp | 61 ++-- Grinder/ui/mainwnd/PropertyTreeItemDelegate.h | 2 + Grinder/ui/mainwnd/PropertyTreeWidget.cpp | 2 +- Grinder/ui/task/ConfigureTaskDialog.cpp | 2 +- Grinder/ui/task/ConfigureTaskDialog.ui | 6 +- Grinder/ui/task/TaskWidget.cpp | 8 + Grinder/ui/task/TaskWidget.h | 1 + Grinder/ui/task/TaskWidget.ui | 4 +- .../task/tasks/BaristaTrainingTaskWidget.cpp | 57 ++++ .../ui/task/tasks/BaristaTrainingTaskWidget.h | 38 +++ .../task/tasks/BaristaTrainingTaskWidget.ui | 140 ++++++++ ...icTaskWidget.cpp => GenericTaskWidget.cpp} | 18 +- ...enericTaskWidget.h => GenericTaskWidget.h} | 14 +- ...ericTaskWidget.ui => GenericTaskWidget.ui} | 4 +- 32 files changed, 1114 insertions(+), 58 deletions(-) create mode 100644 Grinder/task/tasks/BaristaMessage.cpp create mode 100644 Grinder/task/tasks/BaristaMessage.h create mode 100644 Grinder/task/tasks/BaristaProtocol.h create mode 100644 Grinder/task/tasks/BaristaTask.cpp create mode 100644 Grinder/task/tasks/BaristaTask.h create mode 100644 Grinder/task/tasks/BaristaTrainingTask.cpp create mode 100644 Grinder/task/tasks/BaristaTrainingTask.h create mode 100644 Grinder/ui/task/tasks/BaristaTrainingTaskWidget.cpp create mode 100644 Grinder/ui/task/tasks/BaristaTrainingTaskWidget.h create mode 100644 Grinder/ui/task/tasks/BaristaTrainingTaskWidget.ui rename Grinder/ui/task/tasks/{ConfigureGenericTaskWidget.cpp => GenericTaskWidget.cpp} (66%) rename Grinder/ui/task/tasks/{ConfigureGenericTaskWidget.h => GenericTaskWidget.h} (60%) rename Grinder/ui/task/tasks/{ConfigureGenericTaskWidget.ui => GenericTaskWidget.ui} (95%) diff --git a/Grinder/Grinder.pro b/Grinder/Grinder.pro index e3758e0..39328b1 100644 --- a/Grinder/Grinder.pro +++ b/Grinder/Grinder.pro @@ -344,8 +344,12 @@ SOURCES += \ ui/task/TaskWidget.cpp \ ui/dlg/TextViewerDialog.cpp \ ui/task/ConfigureTaskDialog.cpp \ - ui/task/tasks/ConfigureGenericTaskWidget.cpp \ - ui/task/ConfigureTaskWidgetBase.cpp + ui/task/ConfigureTaskWidgetBase.cpp \ + task/tasks/BaristaTask.cpp \ + ui/task/tasks/GenericTaskWidget.cpp \ + task/tasks/BaristaMessage.cpp \ + task/tasks/BaristaTrainingTask.cpp \ + ui/task/tasks/BaristaTrainingTaskWidget.cpp HEADERS += \ ui/mainwnd/GrinderWindow.h \ @@ -734,9 +738,14 @@ HEADERS += \ ui/dlg/TextViewerDialog.h \ ui/task/ConfigureTaskDialog.h \ ui/task/ConfigureTaskWidget.h \ - ui/task/tasks/ConfigureGenericTaskWidget.h \ ui/task/ConfigureTaskWidget.impl.h \ - ui/task/ConfigureTaskWidgetBase.h + ui/task/ConfigureTaskWidgetBase.h \ + task/tasks/BaristaTask.h \ + ui/task/tasks/GenericTaskWidget.h \ + task/tasks/BaristaMessage.h \ + task/tasks/BaristaTrainingTask.h \ + ui/task/tasks/BaristaTrainingTaskWidget.h \ + task/tasks/BaristaProtocol.h FORMS += \ ui/mainwnd/GrinderWindow.ui \ @@ -752,7 +761,8 @@ FORMS += \ ui/task/TaskWidget.ui \ ui/dlg/TextViewerDialog.ui \ ui/task/ConfigureTaskDialog.ui \ - ui/task/tasks/ConfigureGenericTaskWidget.ui + ui/task/tasks/GenericTaskWidget.ui \ + ui/task/tasks/BaristaTrainingTaskWidget.ui RESOURCES += \ res/Grinder.qrc diff --git a/Grinder/Version.h b/Grinder/Version.h index 50990ec..bb9b2eb 100644 --- a/Grinder/Version.h +++ b/Grinder/Version.h @@ -10,14 +10,14 @@ #define GRNDR_INFO_TITLE "Grinder" #define GRNDR_INFO_COPYRIGHT "Copyright (c) WWU Muenster" -#define GRNDR_INFO_DATE "08.11.2018" +#define GRNDR_INFO_DATE "30.11.2018" #define GRNDR_INFO_COMPANY "WWU Muenster" #define GRNDR_INFO_WEBSITE "http://www.uni-muenster.de" #define GRNDR_VERSION_MAJOR 0 -#define GRNDR_VERSION_MINOR 9 +#define GRNDR_VERSION_MINOR 10 #define GRNDR_VERSION_REVISION 0 -#define GRNDR_VERSION_BUILD 284 +#define GRNDR_VERSION_BUILD 290 namespace grndr { diff --git a/Grinder/common/serialization/JsonSettingsCodec.cpp b/Grinder/common/serialization/JsonSettingsCodec.cpp index 699ce22..4ee5c7b 100644 --- a/Grinder/common/serialization/JsonSettingsCodec.cpp +++ b/Grinder/common/serialization/JsonSettingsCodec.cpp @@ -149,7 +149,7 @@ std::vector<JsonSettingsCodec::Token> JsonSettingsCodec::readJsonDocument(QTextS TokenizerEntry{R"(\])", TokenType::ArrayEnd}, TokenizerEntry{R"(:)", TokenType::Colon}, TokenizerEntry{R"(,)", TokenType::Comma}, - TokenizerEntry{R"([^,]+)", TokenType::Value}, + TokenizerEntry{R"([^,{}]+)", TokenType::Value}, }; // Tokenize the entire document, line by line diff --git a/Grinder/common/serialization/SettingsContainer.cpp b/Grinder/common/serialization/SettingsContainer.cpp index c8f2239..7d8bc3f 100644 --- a/Grinder/common/serialization/SettingsContainer.cpp +++ b/Grinder/common/serialization/SettingsContainer.cpp @@ -46,8 +46,14 @@ SettingsContainer& SettingsContainer::operator <<(const SettingsContainer& child return *this; } -SettingsContainer* SettingsContainer::createChild(QString name, bool isArray) +SettingsContainer* SettingsContainer::createChildEx(QString name, bool unique, bool isArray) { + if (unique) + { + if (auto childContainer = child(name)) + return childContainer; + } + auto container = std::make_shared<SettingsContainer>(name, isArray); _childContainers.push_back(container); return container.get(); @@ -75,3 +81,14 @@ QVariant& SettingsContainer::value(QString name, QVariant defaultValue) return _values[name]; } + +bool SettingsContainer::contains(QStringList names) const +{ + for (QString name : names) + { + if (!contains(name)) + return false; + } + + return true; +} diff --git a/Grinder/common/serialization/SettingsContainer.h b/Grinder/common/serialization/SettingsContainer.h index dc035af..f518a75 100644 --- a/Grinder/common/serialization/SettingsContainer.h +++ b/Grinder/common/serialization/SettingsContainer.h @@ -37,7 +37,8 @@ namespace grndr SettingsContainer& operator <<(const SettingsContainer& child); SettingsContainer& operator <<(SettingsContainer&& child); - SettingsContainer* createChild(QString name, bool isArray = false); + SettingsContainer* createChild(QString name, bool isArray = false) { return createChildEx(name, false, isArray); } + SettingsContainer* createChildEx(QString name, bool unique, bool isArray = false); SettingsContainer* child(QString name) { return _child<SettingsContainer*>(name); } const SettingsContainer* child(QString name) const { return _child<const SettingsContainer*>(name); } @@ -60,6 +61,7 @@ namespace grndr QStringList keys() const { return _values.keys(); } bool contains(QString name) const { return _values.find(name) != _values.cend(); } + bool contains(QStringList names) const; void removeValue(QString name) { _values.remove(name); } void clearValues() { _values.clear(); } diff --git a/Grinder/task/Task.h b/Grinder/task/Task.h index 56cc9ec..1934336 100644 --- a/Grinder/task/Task.h +++ b/Grinder/task/Task.h @@ -63,7 +63,7 @@ namespace grndr void pauseTask(bool setPause = true); void refreshTask(); void stopTask(); - void finishTask(bool succeeded = false); + void finishTask(bool succeeded); void updateTask(); diff --git a/Grinder/task/TaskCatalog.cpp b/Grinder/task/TaskCatalog.cpp index 2eb7482..18a51aa 100644 --- a/Grinder/task/TaskCatalog.cpp +++ b/Grinder/task/TaskCatalog.cpp @@ -8,6 +8,7 @@ #include "Task.h" #include "tasks/GenericTask.h" +#include "tasks/BaristaTrainingTask.h" #define REGISTER_TASK_TYPE(cls) registerTaskType(cls::type_value, [](TaskPool* taskPool, QString name) { return std::make_unique<cls>(taskPool, name); }) @@ -56,4 +57,5 @@ std::unique_ptr<Task> TaskCatalog::createTask(TaskPool* taskPool, TaskType type, void TaskCatalog::registerStandardTasks() { REGISTER_TASK_TYPE(GenericTask); + REGISTER_TASK_TYPE(BaristaTrainingTask); } diff --git a/Grinder/task/TaskType.cpp b/Grinder/task/TaskType.cpp index fa33a20..3ac136c 100644 --- a/Grinder/task/TaskType.cpp +++ b/Grinder/task/TaskType.cpp @@ -9,3 +9,5 @@ const char* TaskType::Undefined = ""; const char* TaskType::Generic = "Generic"; + +const char* TaskType::BaristaTraining = "BaristaTraining"; diff --git a/Grinder/task/TaskType.h b/Grinder/task/TaskType.h index 68476d9..ef4b5d9 100644 --- a/Grinder/task/TaskType.h +++ b/Grinder/task/TaskType.h @@ -17,6 +17,8 @@ namespace grndr static const char* Generic; + static const char* BaristaTraining; + public: using QString::QString; diff --git a/Grinder/task/tasks/BaristaMessage.cpp b/Grinder/task/tasks/BaristaMessage.cpp new file mode 100644 index 0000000..d9d91f6 --- /dev/null +++ b/Grinder/task/tasks/BaristaMessage.cpp @@ -0,0 +1,53 @@ +/****************************************************************************** + * File: BaristaMessage.cpp + * Date: 25.11.2018 + *****************************************************************************/ + +#include "Grinder.h" +#include "BaristaMessage.h" + +#define BARISTA_MESSAGE_HEADER "header" +#define BARISTA_MESSAGE_HEADER_BLOCKING "blocking" +#define BARISTA_MESSAGE_PAYLOAD "payload" +#define BARISTA_MESSAGE_PAYLOAD_KEY "key" + +void BaristaMessage::createMessage(QString key, bool blocking) +{ + if (auto headerData = _messageData.createChildEx(BARISTA_MESSAGE_HEADER, true)) + headerData->value(BARISTA_MESSAGE_HEADER_BLOCKING) = blocking; + + if (auto payloadData = _messageData.createChildEx(BARISTA_MESSAGE_PAYLOAD, true)) + payloadData->value(BARISTA_MESSAGE_PAYLOAD_KEY) = key; +} + +bool BaristaMessage::isBlocking() const +{ + if (auto headerData = _messageData.child(BARISTA_MESSAGE_HEADER)) + return headerData->value(BARISTA_MESSAGE_HEADER_BLOCKING, false).toBool(); + + return false; +} + +QString BaristaMessage::getPayloadKey() const +{ + if (auto payloadData = _messageData.child(BARISTA_MESSAGE_PAYLOAD)) + return payloadData->value(BARISTA_MESSAGE_PAYLOAD_KEY).toString(); + + return ""; +} + +bool BaristaMessage::isMessage() const +{ + // Make sure that the message contains a header and a payload + return _messageData.child(BARISTA_MESSAGE_HEADER) && _messageData.child(BARISTA_MESSAGE_PAYLOAD); +} + +bool BaristaMessage::isMessage(QString key) const +{ + return isMessage() && (getPayloadKey().compare(key, Qt::CaseInsensitive) == 0); +} + +SettingsContainer& BaristaMessage::payload() +{ + return *_messageData.createChildEx(BARISTA_MESSAGE_PAYLOAD, true); +} diff --git a/Grinder/task/tasks/BaristaMessage.h b/Grinder/task/tasks/BaristaMessage.h new file mode 100644 index 0000000..a8c3a83 --- /dev/null +++ b/Grinder/task/tasks/BaristaMessage.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * File: BaristaMessage.h + * Date: 25.11.2018 + *****************************************************************************/ + +#ifndef BARISTAMESSAGE_H +#define BARISTAMESSAGE_H + +#include "common/serialization/SettingsContainer.h" + +namespace grndr +{ + class BaristaMessage + { + public: + BaristaMessage() { } + BaristaMessage(QString key, bool blocking = false) { createMessage(key, blocking); } + BaristaMessage(const SettingsContainer& settings) { createMessage(settings); } + BaristaMessage(SettingsContainer&& settings) { createMessage(std::move(settings)); } + + operator SettingsContainer() const { return _messageData; } + operator const SettingsContainer&() const { return _messageData; } + + public: + void createMessage(QString key, bool blocking = false); + void createMessage(const SettingsContainer& settings) { _messageData = settings; } + void createMessage(SettingsContainer&& settings) { _messageData = std::move(settings); } + + public: + bool isBlocking() const; + + QString getPayloadKey() const; + SettingsContainer& payload(); + + const SettingsContainer& messageData() const { return _messageData; } + + bool isMessage() const; + bool isMessage(QString key) const; + + private: + SettingsContainer _messageData; + }; +} + +#endif diff --git a/Grinder/task/tasks/BaristaProtocol.h b/Grinder/task/tasks/BaristaProtocol.h new file mode 100644 index 0000000..abbdef9 --- /dev/null +++ b/Grinder/task/tasks/BaristaProtocol.h @@ -0,0 +1,15 @@ +/****************************************************************************** + * File: BaristaProtocol.h + * Date: 30.11.2018 + *****************************************************************************/ + +#ifndef BARISTAPROTOCOL_H +#define BARISTAPROTOCOL_H + +#define BARISTA_COMMAND_SETLIBRARY "setlibrarypath" +#define BARISTA_COMMAND_STARTTRAINING "starttraining" +#define BARISTA_COMMAND_UPDATE "iterationupdate" +#define BARISTA_COMMAND_TRAININGDONE "trainingfinished" +#define BARISTA_COMMAND_SHUTDOWN "shutdown" + +#endif diff --git a/Grinder/task/tasks/BaristaTask.cpp b/Grinder/task/tasks/BaristaTask.cpp new file mode 100644 index 0000000..50d13e6 --- /dev/null +++ b/Grinder/task/tasks/BaristaTask.cpp @@ -0,0 +1,312 @@ +/****************************************************************************** + * File: BaristaTask.cpp + * Date: 20.11.2018 + *****************************************************************************/ + +#include "Grinder.h" +#include "BaristaTask.h" +#include "BaristaMessage.h" +#include "BaristaProtocol.h" +#include "task/TaskExceptions.h" +#include "common/serialization/JsonSettingsCodec.h" +#include "common/serialization/SerializationExceptions.h" + +const char* BaristaTask::Serialization_Value_BaristaPort = "BaristaPort"; +const char* BaristaTask::Serialization_Value_LibraryPath = "LibraryPath"; + +BaristaTask::BaristaTask(TaskPool* taskPool, TaskType type, QString name) : Task(taskPool, type, Task::Capability::All, name) +{ + // When the task has been stopped, immediately finish it + connect(this, &Task::taskStopped, [this]() { finishTask(false); }); +} + +void BaristaTask::serialize(SerializationContext& ctx) const +{ + Task::serialize(ctx); + + // Serialize values + ctx.settings()(Serialization_Value_BaristaPort) = _baristaPort; + ctx.settings()(Serialization_Value_LibraryPath) = _libraryPath; +} + +void BaristaTask::deserialize(DeserializationContext& ctx) +{ + Task::deserialize(ctx); + + // Deserialize values + _baristaPort = ctx.settings()(Serialization_Value_BaristaPort).toUInt(); + _libraryPath = ctx.settings()(Serialization_Value_LibraryPath).toString(); +} + +void BaristaTask::initiateBaristaConnection() +{ + changeTaskState(TaskState::Initiating); + + // Create the ZMQ objects + try { + _context = std::make_unique<zmq::context_t>(1); + + auto createSocket = [this](int type) { + auto socket = std::make_unique<zmq::socket_t>(*_context, type); + + // Make sure that all pending messages are sent on close + int linger = -1; + socket->setsockopt(ZMQ_LINGER, &linger, sizeof(linger)); + + return socket; + }; + + _replySocket = createSocket(ZMQ_REP); + _subscriberSocket = createSocket(ZMQ_SUB); + _requestSocket = createSocket(ZMQ_REQ); + + // The subscriber should just receive everything + _subscriberSocket->setsockopt(ZMQ_SUBSCRIBE, "", 0); + + // Bind the reply port + _replySocket->bind(QString{"tcp://*:%1"}.arg(_baristaPort).toStdString()); + } catch (std::exception& e) { + throw TaskException{this, _EXCPT(QString{"Unable to create the ZMQ objects (%1)"}.arg(e.what()))}; + } + + // Enter the awaiting connection state + changeTaskState(TaskState::AwaitingConnection, "Waiting for Barista to connect..."); +} + +void BaristaTask::shutdownBaristaConnection(bool shutdownBarista) +{ + changeTaskState(TaskState::Shutdown, "Shutting down Barista connection..."); + + if (shutdownBarista) + { + if (_requestSocket && _taskState > TaskState::Initiating) + { + BaristaMessage shutdownMessage{BARISTA_COMMAND_SHUTDOWN}; + sendMessage(shutdownMessage, _requestSocket); + } + } + + // Just set all pointers to zero and let RAII handle the rest + _replySocket = nullptr; + _requestSocket = nullptr; + _subscriberSocket = nullptr; + + _context = nullptr; +} + +void BaristaTask::reportBaristaError(QString error, BaristaMessage* message) +{ + if (message) + { + QString baristaError = message->payload().value("error").toString(); + + if (!baristaError.isEmpty()) + error += QString{" (%1)"}.arg(baristaError); + } + + addMessageLog(error); + + shutdownBaristaConnection(); + finishTask(false); +} + +bool BaristaTask::encodeMessage(zmq::message_t& message, const SettingsContainer& settings) +{ + try { + // Encode the settings to JSON and store it in the ZMQ message object + QString messageData; + QTextStream textStream{&messageData}; + JsonSettingsCodec codec; + + codec.encodeSettings(settings, textStream); + + // Use a std::string as QString uses a 16bit representation of chars + std::string messageString = messageData.toStdString(); + message.rebuild(messageString.size()); + memcpy(message.data(), messageString.data(), messageString.size()); + } catch (SerializationException& e) { + addMessageLog(QString{"! Unable to encode a message (%1)"}.arg(GetExceptionMessage(e.what()))); + return false; + } catch (std::exception& e) { + addMessageLog(QString{"! Unable to encode a message (%1)"}.arg(e.what())); + return false; + } + + return true; +} + +bool BaristaTask::decodeMessage(const zmq::message_t& message, SettingsContainer& settings) +{ + try { + // Convert the ZMQ message to a SettingsContainer + auto messageData = QString::fromLatin1(static_cast<const char*>(message.data()), static_cast<int>(message.size())); + QTextStream textStream{&messageData}; + JsonSettingsCodec codec; + + settings.clear(); + codec.decodeSettings(settings, textStream); + } catch (SerializationException& e) { + addMessageLog(QString{"! Unable to decode a message (%1)"}.arg(GetExceptionMessage(e.what()))); + return false; + } catch (std::exception& e) { + addMessageLog(QString{"! Unable to decode a message (%1)"}.arg(e.what())); + return false; + } + + return true; +} + +void BaristaTask::sendMessage(const SettingsContainer& settings, std::unique_ptr<zmq::socket_t>& socket, bool receiveAck) +{ + // Just encode the message and send it over the socket + zmq::message_t message; + + if (encodeMessage(message, settings)) + { + try { + socket->send(message); + } catch (std::exception& e) { + // Ignore ZMQ errors here + } + + if (receiveAck) + { + // Receive a corresponding ACK message from Barista; we are not really interested in it, though, so we just ignore it + zmq::message_t ackMessage; + socket->recv(&ackMessage); + } + } +} + +bool BaristaTask::checkMessageStatus(BaristaMessage& message) const +{ + return message.payload()("status", false).toBool(); +} + +bool BaristaTask::handleReplyMessage(const SettingsContainer& messageData) +{ + switch (_taskState) + { + case TaskState::AwaitingConnection: + handleAwaitingConnection(messageData); + return true; + } + + return false; +} + +bool BaristaTask::handleSubscriberMessage(const SettingsContainer& messageData) +{ + switch (_taskState) + { + case TaskState::SettingLibrary: + handleSettingLibrary(messageData); + return true; + } + + return false; +} + +void BaristaTask::update() +{ + if (_taskState > TaskState::Initiating) + { + // Read messages from all sockets + if (_replySocket) + pollMessage(_replySocket, &BaristaTask::handleReplyMessage); + + if (_subscriberSocket) + pollMessage(_subscriberSocket, &BaristaTask::handleSubscriberMessage); + } +} + +void BaristaTask::pollMessage(std::unique_ptr<zmq::socket_t>& socket, std::function<void(BaristaTask*, const SettingsContainer&)> callback) +{ + // Poll the given socket for a message + zmq::pollitem_t pollItems[] = {{*socket, 0, ZMQ_POLLIN, 0}}; + zmq::poll(&pollItems[0], 1, 0); + + if (pollItems[0].revents & ZMQ_POLLIN) + { + // Some data is waiting on the port, so receive it + zmq::message_t message; + socket->recv(&message); + + if (callback && message.size() > 0) + { + SettingsContainer messageSettings; + + if (decodeMessage(message, messageSettings)) + { + try { + callback(this, messageSettings); + } catch (TaskException& e) { + // Show errors but ignore them otherwise + addMessageLog(QString{"! %1"}.arg(GetExceptionMessage(e.what()))); + } + } + } + } +} + +void BaristaTask::handleAwaitingConnection(const SettingsContainer& messageData) +{ + // Get the Barista address from the reply + _baristaAddress.ip = messageData("ip").toString(); + _baristaAddress.replyPort = messageData("repPort").toInt(); + _baristaAddress.publisherPort = messageData("pubPort").toInt(); + + if (_baristaAddress.ip.isEmpty() || _baristaAddress.replyPort < 1024 || _baristaAddress.publisherPort < 1024) + throw TaskException{this, _EXCPT("No valid Barista address could be retrieved")}; + + // Establish the connection to Barista + _requestSocket->connect(QString{"tcp://%1:%2"}.arg(_baristaAddress.ip).arg(_baristaAddress.replyPort).toStdString()); + _subscriberSocket->connect(QString{"tcp://%1:%2"}.arg(_baristaAddress.ip).arg(_baristaAddress.publisherPort).toStdString()); + + addMessageLog(QString{"\tConnected to Barista at %1"}.arg(_baristaAddress.ip)); + + baristaConnected(); + + // Set the library to be used by Barista + changeTaskState(TaskState::SettingLibrary, "Setting up the Barista library..."); + sendSetLibraryMessage(); +} + +void BaristaTask::handleSettingLibrary(const SettingsContainer& messageData) +{ + BaristaMessage message{messageData}; + + if (message.isMessage(BARISTA_COMMAND_SETLIBRARY)) + { + if (checkMessageStatus(message)) + { + addMessageLog(QString{"\tLibrary set to %1"}.arg(_libraryPath)); + + // Let the task-specific implementation handle the rest from here on + baristaReady(); + } + else + reportBaristaError(QString{"\tFailed to set library to %1"}.arg(_libraryPath), &message); + } +} + +void BaristaTask::sendSetLibraryMessage() +{ + BaristaMessage message{BARISTA_COMMAND_SETLIBRARY}; + SettingsContainer data{"data"}; + data("path") = _libraryPath; + message.payload() << std::move(data); + + sendMessage(message, _requestSocket); +} + +void BaristaTask::changeTaskState(int state, QString msg) +{ + if (state != _taskState) + { + _taskState = state; + + if (!msg.isEmpty()) + addMessageLog(msg); + } +} diff --git a/Grinder/task/tasks/BaristaTask.h b/Grinder/task/tasks/BaristaTask.h new file mode 100644 index 0000000..a640ec8 --- /dev/null +++ b/Grinder/task/tasks/BaristaTask.h @@ -0,0 +1,109 @@ +/****************************************************************************** + * File: BaristaTask.h + * Date: 20.11.2018 + *****************************************************************************/ + +#ifndef BARISTATASK_H +#define BARISTATASK_H + +#include <zmq.hpp> + +#include "task/Task.h" + +namespace grndr +{ + class BaristaMessage; + + class BaristaTask : public Task + { + Q_OBJECT + + public: + static const char* Serialization_Value_BaristaPort; + static const char* Serialization_Value_LibraryPath; + + public: + BaristaTask(TaskPool* taskPool, TaskType type, QString name = ""); + virtual ~BaristaTask() { shutdownBaristaConnection(); } + + public: + unsigned int getBaristaPort() const { return _baristaPort; } + void setBaristaPort(unsigned int port) { _baristaPort = port; } + + QString getLibraryPath() const { return _libraryPath; } + void setLibraryPath(QString path) { _libraryPath = path; } + + public: + virtual void serialize(SerializationContext& ctx) const override; + virtual void deserialize(DeserializationContext& ctx) override; + + protected: + void initiateBaristaConnection(); + void shutdownBaristaConnection(bool shutdownBarista = true); + + void reportBaristaError(QString error, BaristaMessage* message = nullptr); + + protected: + bool encodeMessage(zmq::message_t& message, const SettingsContainer& settings); + bool decodeMessage(const zmq::message_t& message, SettingsContainer& settings); + void sendMessage(const SettingsContainer& settings, std::unique_ptr<zmq::socket_t>& socket, bool receiveAck = true); + + bool checkMessageStatus(BaristaMessage& message) const; + + protected: + virtual void baristaConnected() { } + virtual void baristaReady() { } + + virtual bool handleReplyMessage(const SettingsContainer& messageData); + virtual bool handleSubscriberMessage(const SettingsContainer& messageData); + + protected: + virtual void update() override; + + private: + void pollMessage(std::unique_ptr<zmq::socket_t>& socket, std::function<void(BaristaTask*, const SettingsContainer&)> callback); + + private: + void handleAwaitingConnection(const SettingsContainer& messageData); + void handleSettingLibrary(const SettingsContainer& messageData); + + void sendSetLibraryMessage(); + + protected: + unsigned int _baristaPort{6980}; + + struct + { + QString ip{""}; + unsigned int replyPort{0}; + unsigned int publisherPort{0}; + } _baristaAddress; + + QString _libraryPath{""}; + + protected: + enum TaskState + { + Shutdown, + Initiating, + AwaitingConnection, + SettingLibrary, + + // Must always be the last state + TypeSpecificBase, + }; + + int _taskState{TaskState::Shutdown}; + + void changeTaskState(int state, QString msg = ""); + + protected: + std::unique_ptr<zmq::context_t> _context; + + std::unique_ptr<zmq::socket_t> _replySocket; + std::unique_ptr<zmq::socket_t> _subscriberSocket; + std::unique_ptr<zmq::socket_t> _requestSocket; + }; +} + +#endif diff --git a/Grinder/task/tasks/BaristaTrainingTask.cpp b/Grinder/task/tasks/BaristaTrainingTask.cpp new file mode 100644 index 0000000..8bc5015 --- /dev/null +++ b/Grinder/task/tasks/BaristaTrainingTask.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** + * File: BaristaCaffeWorkerTask.cpp + * Date: 20.11.2018 + *****************************************************************************/ + +#include "Grinder.h" +#include "BaristaTrainingTask.h" +#include "BaristaMessage.h" +#include "BaristaProtocol.h" +#include "ui/task/tasks/BaristaTrainingTaskWidget.h" + +const TaskType BaristaTrainingTask::type_value = TaskType::BaristaTraining; + +const char* BaristaTrainingTask::Serialization_Value_SessionPath = "SessionPath"; +const char* BaristaTrainingTask::Serialization_Value_SolverPath = "SolverPath"; + +BaristaTrainingTask::BaristaTrainingTask(TaskPool* taskPool, QString name) : BaristaTask(taskPool, type_value, name) +{ + +} + +ConfigureTaskWidgetBase* BaristaTrainingTask::createEditor(bool newTask, QWidget* parent) +{ + return new BaristaTrainingTaskWidget{this, newTask, parent}; +} + +void BaristaTrainingTask::serialize(SerializationContext& ctx) const +{ + BaristaTask::serialize(ctx); + + // Serialize values + ctx.settings()(Serialization_Value_SessionPath) = _sessionPath; + ctx.settings()(Serialization_Value_SolverPath) = _solverPath; +} + +void BaristaTrainingTask::deserialize(DeserializationContext& ctx) +{ + BaristaTask::deserialize(ctx); + + // Deserialize values + _sessionPath = ctx.settings()(Serialization_Value_SessionPath).toString(); + _solverPath = ctx.settings()(Serialization_Value_SolverPath).toString(); +} + +void BaristaTrainingTask::execute() +{ + initiateBaristaConnection(); +} + +void BaristaTrainingTask::stop() +{ + shutdownBaristaConnection(); +} + +void BaristaTrainingTask::baristaReady() +{ + // Start the training + changeTaskState(WorkerTaskState::StartTraining, "Starting training..."); + sendStartTrainingMessage(); +} + +bool BaristaTrainingTask::handleReplyMessage(const SettingsContainer& messageData) +{ + if (BaristaTask::handleReplyMessage(messageData)) + return true; + + return false; +} + +bool BaristaTrainingTask::handleSubscriberMessage(const SettingsContainer& messageData) +{ + if (BaristaTask::handleSubscriberMessage(messageData)) + return true; + + switch (_taskState) + { + case WorkerTaskState::StartTraining: + handleStartingTraining(messageData); + return true; + + case WorkerTaskState::Training: + handleTraining(messageData); + return true; + } + + return false; +} + +void BaristaTrainingTask::handleStartingTraining(const SettingsContainer& messageData) +{ + BaristaMessage message{messageData}; + + if (message.isMessage(BARISTA_COMMAND_STARTTRAINING)) + { + if (checkMessageStatus(message)) + { + addMessageLog("\tTraining started"); + addMessageLog("", false); + + // Just let the training run... + changeTaskState(WorkerTaskState::Training); + } + else + reportBaristaError("\tFailed to start the training", &message); + } +} + +void BaristaTrainingTask::handleTraining(const SettingsContainer& messageData) +{ + BaristaMessage message{messageData}; + + if (message.isMessage(BARISTA_COMMAND_UPDATE)) + { + if (auto data = message.payload().child("data")) + { + // Just log some status information + auto iteration = data->value("iteration").toUInt(); + auto eta = data->value("eta").toFloat(); + + addMessageLog(QString{"Training iteration %1 [ETA %2]"}.arg(iteration).arg(QTime{0, 0}.addSecs(static_cast<int>(std::ceil(eta))).toString("HH:mm:ss"))); + } + } + else if (message.isMessage(BARISTA_COMMAND_TRAININGDONE)) + { + addMessageLog("", false); + + if (checkMessageStatus(message)) + { + addMessageLog("The training has been successfully finished"); + + // The training has finished, so break the Barista connection and finish the task + shutdownBaristaConnection(); + finishTask(true); + } + else + reportBaristaError("The training did not finish successfully", &message); + } +} + +void BaristaTrainingTask::sendStartTrainingMessage() +{ + BaristaMessage message{BARISTA_COMMAND_STARTTRAINING}; + SettingsContainer data{"data"}; + data("dir") = _sessionPath; + data("solver") = _solverPath; + message.payload() << std::move(data); + + sendMessage(message, _requestSocket); +} diff --git a/Grinder/task/tasks/BaristaTrainingTask.h b/Grinder/task/tasks/BaristaTrainingTask.h new file mode 100644 index 0000000..4b6cf8c --- /dev/null +++ b/Grinder/task/tasks/BaristaTrainingTask.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * File: BaristaCaffeWorkerTask.h + * Date: 20.11.2018 + *****************************************************************************/ + +#ifndef BARISTATRAININGTASK_H +#define BARISTATRAININGTASK_H + +#include "BaristaTask.h" + +namespace grndr +{ + class BaristaTrainingTask : public BaristaTask + { + Q_OBJECT + + public: + static const TaskType type_value; + + static const char* Serialization_Value_SessionPath; + static const char* Serialization_Value_SolverPath; + + public: + BaristaTrainingTask(TaskPool* taskPool, QString name = ""); + + public: + virtual ConfigureTaskWidgetBase* createEditor(bool newTask, QWidget* parent) override; + + public: + QString getSessionPath() const { return _sessionPath; } + void setSessionPath(QString path) { _sessionPath = path; } + QString getSolverPath() const { return _solverPath; } + void setSolverPath(QString path) { _solverPath = path; } + + public: + virtual void serialize(SerializationContext& ctx) const override; + virtual void deserialize(DeserializationContext& ctx) override; + + protected: + virtual void execute() override; + virtual void stop() override; + + protected: + virtual void baristaReady() override; + + virtual bool handleReplyMessage(const SettingsContainer& messageData) override; + virtual bool handleSubscriberMessage(const SettingsContainer& messageData) override; + + protected: + void handleStartingTraining(const SettingsContainer& messageData); + void handleTraining(const SettingsContainer& messageData); + + void sendStartTrainingMessage(); + + protected: + enum WorkerTaskState + { + StartTraining = TypeSpecificBase, + Training, + }; + + protected: + QString _sessionPath{""}; + QString _solverPath{""}; + }; +} + +#endif diff --git a/Grinder/task/tasks/GenericTask.cpp b/Grinder/task/tasks/GenericTask.cpp index 2640952..b7b13c4 100644 --- a/Grinder/task/tasks/GenericTask.cpp +++ b/Grinder/task/tasks/GenericTask.cpp @@ -6,7 +6,7 @@ #include "Grinder.h" #include "GenericTask.h" #include "task/TaskExceptions.h" -#include "ui/task/tasks/ConfigureGenericTaskWidget.h" +#include "ui/task/tasks/GenericTaskWidget.h" const TaskType GenericTask::type_value = TaskType::Generic; @@ -20,7 +20,7 @@ GenericTask::GenericTask(TaskPool* taskPool, QString name) : Task(taskPool, type ConfigureTaskWidgetBase* GenericTask::createEditor(bool newTask, QWidget* parent) { - return new ConfigureGenericTaskWidget{this, newTask, parent}; + return new GenericTaskWidget{this, newTask, parent}; } void GenericTask::serialize(SerializationContext& ctx) const diff --git a/Grinder/ui/dlg/TextViewerDialog.cpp b/Grinder/ui/dlg/TextViewerDialog.cpp index aa6f7da..0e18ab1 100644 --- a/Grinder/ui/dlg/TextViewerDialog.cpp +++ b/Grinder/ui/dlg/TextViewerDialog.cpp @@ -31,6 +31,7 @@ void TextViewerDialog::setupUi(QString title, QString caption, QString text, QSt setWindowTitle(title); ui->lblCaption->setText(caption); + ui->textField->setTabStopDistance(40); if (!text.isEmpty()) ui->textField->setPlainText(text); diff --git a/Grinder/ui/mainwnd/PropertyTreeItemDelegate.cpp b/Grinder/ui/mainwnd/PropertyTreeItemDelegate.cpp index 280048d..266cc51 100644 --- a/Grinder/ui/mainwnd/PropertyTreeItemDelegate.cpp +++ b/Grinder/ui/mainwnd/PropertyTreeItemDelegate.cpp @@ -29,14 +29,17 @@ void PropertyTreeItemDelegate::paint(QPainter* painter, const QStyleOptionViewIt { bool performDefault = true; - if (auto item = _widget->valuePropertyItem(index)) + if (index.column() == 1) { - if (auto renderer = item->valueRenderer()) + if (auto item = _widget->valuePropertyItem(index)) { - if (renderer->getRendererFlags().testFlag(PropertyRenderer::RendererFlag::OverridePainting)) + if (auto renderer = item->valueRenderer()) { - renderer->render(painter, rendererStyleFromStyleOption(option), renderFlagsFromStyleOption(option)); - performDefault = false; + if (renderer->getRendererFlags().testFlag(PropertyRenderer::RendererFlag::OverridePainting)) + { + renderer->render(painter, rendererStyleFromStyleOption(option), renderFlagsFromStyleOption(option)); + performDefault = false; + } } } } @@ -49,15 +52,18 @@ QWidget* PropertyTreeItemDelegate::createEditor(QWidget* parent, const QStyleOpt { Q_UNUSED(option); - if (auto item = _widget->valuePropertyItem(index)) + if (index.column() == 1) { - if (auto editor = item->createEditor(parent)) + if (auto item = _widget->valuePropertyItem(index)) { - editor->setProperty(PropertyEditorBase::property_font, _widget->font()); // Forward the tree widget's font to the editor as a dynamic property - editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + if (auto editor = item->createEditor(parent)) + { + editor->setProperty(PropertyEditorBase::property_font, _widget->font()); // Forward the tree widget's font to the editor as a dynamic property + editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - _currentEditor = editor; - return editor; + _currentEditor = editor; + return editor; + } } } @@ -86,18 +92,21 @@ void PropertyTreeItemDelegate::initStyleOption(QStyleOptionViewItem* option, con { QStyledItemDelegate::initStyleOption(option, index); - if (auto item = _widget->valuePropertyItem(index)) + if (index.column() == 1) { - if (auto renderer = item->valueRenderer()) + if (auto item = _widget->valuePropertyItem(index)) { - if (renderer->getRendererFlags().testFlag(PropertyRenderer::RendererFlag::OverrideText)) - option->text = renderer->formatPropertyText(renderFlagsFromStyleOption(*option)); + if (auto renderer = item->valueRenderer()) + { + if (renderer->getRendererFlags().testFlag(PropertyRenderer::RendererFlag::OverrideText)) + option->text = renderer->formatPropertyText(renderFlagsFromStyleOption(*option)); + } } - } - // If editing the given item, hide its text so that it doesn't appear below the editor - if (_widget->isEditing() && index.row() == _widget->currentIndex().row()) - option->text = ""; + // If editing the given item, hide its text so that it doesn't appear below the editor + if (_widget->isEditing() && (getAbsoluteRowIndex(index) == getAbsoluteRowIndex(_widget->currentIndex()))) + option->text = ""; + } } PropertyRenderer::RendererStyle PropertyTreeItemDelegate::rendererStyleFromStyleOption(const QStyleOptionViewItem& option) const @@ -120,3 +129,17 @@ PropertyRenderer::RenderFlags PropertyTreeItemDelegate::renderFlagsFromStyleOpti return flags; } + +int PropertyTreeItemDelegate::getAbsoluteRowIndex(const QModelIndex& index) const +{ + int row = 0; + auto indexAbove = _widget->indexAbove(index); + + while (indexAbove.isValid()) + { + row += 1; + indexAbove = _widget->indexAbove(indexAbove); + } + + return row; +} diff --git a/Grinder/ui/mainwnd/PropertyTreeItemDelegate.h b/Grinder/ui/mainwnd/PropertyTreeItemDelegate.h index 2571b72..6d15288 100644 --- a/Grinder/ui/mainwnd/PropertyTreeItemDelegate.h +++ b/Grinder/ui/mainwnd/PropertyTreeItemDelegate.h @@ -40,6 +40,8 @@ namespace grndr PropertyRenderer::RendererStyle rendererStyleFromStyleOption(const QStyleOptionViewItem& option) const; PropertyRenderer::RenderFlags renderFlagsFromStyleOption(const QStyleOptionViewItem& option) const; + int getAbsoluteRowIndex(const QModelIndex& index) const; + private: PropertyTreeWidget* _widget{nullptr}; diff --git a/Grinder/ui/mainwnd/PropertyTreeWidget.cpp b/Grinder/ui/mainwnd/PropertyTreeWidget.cpp index b62d82d..a031e57 100644 --- a/Grinder/ui/mainwnd/PropertyTreeWidget.cpp +++ b/Grinder/ui/mainwnd/PropertyTreeWidget.cpp @@ -18,6 +18,7 @@ PropertyTreeWidget::PropertyTreeWidget(QWidget *parent) : QTreeWidget(parent), _itemDelegate{new PropertyTreeItemDelegate{this}} { // Set the delegate for the second column which handles editing of the properties + setItemDelegateForColumn(0, _itemDelegate); setItemDelegateForColumn(1, _itemDelegate); // Listen for pipeline switches to clear all shown properties and to listen for selection changes @@ -49,7 +50,6 @@ void PropertyTreeWidget::setupUi(QLabel* propertyDescLabel) void PropertyTreeWidget::clear() { - _itemDelegate->resetCurrentEditor(); for (int i = 0; i < topLevelItemCount(); ++i) diff --git a/Grinder/ui/task/ConfigureTaskDialog.cpp b/Grinder/ui/task/ConfigureTaskDialog.cpp index 37c9cc9..8332d49 100644 --- a/Grinder/ui/task/ConfigureTaskDialog.cpp +++ b/Grinder/ui/task/ConfigureTaskDialog.cpp @@ -36,7 +36,7 @@ void ConfigureTaskDialog::setupUi() if (_taskWidget) { - ui->grpTaskSettings->setLayout(new QGridLayout{}); + ui->grpTaskSettings->setLayout(new QHBoxLayout{}); ui->grpTaskSettings->layout()->addWidget(_taskWidget); _taskWidget->applySettings(false); diff --git a/Grinder/ui/task/ConfigureTaskDialog.ui b/Grinder/ui/task/ConfigureTaskDialog.ui index 82df21a..7f9f06d 100644 --- a/Grinder/ui/task/ConfigureTaskDialog.ui +++ b/Grinder/ui/task/ConfigureTaskDialog.ui @@ -9,8 +9,8 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>217</height> + <width>358</width> + <height>140</height> </rect> </property> <property name="windowTitle"> @@ -21,7 +21,7 @@ </property> <layout class="QVBoxLayout" name="verticalLayout"> <property name="sizeConstraint"> - <enum>QLayout::SetFixedSize</enum> + <enum>QLayout::SetMinimumSize</enum> </property> <item> <widget class="QGroupBox" name="groupBox"> diff --git a/Grinder/ui/task/TaskWidget.cpp b/Grinder/ui/task/TaskWidget.cpp index 0060453..d61344d 100644 --- a/Grinder/ui/task/TaskWidget.cpp +++ b/Grinder/ui/task/TaskWidget.cpp @@ -29,6 +29,12 @@ TaskWidget::~TaskWidget() delete ui; } +void TaskWidget::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + updateUi(); +} + void TaskWidget::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); @@ -48,6 +54,8 @@ void TaskWidget::setupUi() void TaskWidget::updateUi() { static auto setEllidedText = [](QLabel* label, QString text) { + text.replace("\t", ""); + QFontMetrics metrics(label->font()); int width = label->width() - 2; QString clippedText = metrics.elidedText(text, Qt::ElideRight, width); diff --git a/Grinder/ui/task/TaskWidget.h b/Grinder/ui/task/TaskWidget.h index 18bf6bd..584d794 100644 --- a/Grinder/ui/task/TaskWidget.h +++ b/Grinder/ui/task/TaskWidget.h @@ -30,6 +30,7 @@ namespace grndr const Task* task() const { return _task.lock().get(); } protected: + virtual void showEvent(QShowEvent* event) override; virtual void resizeEvent(QResizeEvent* event) override; private: diff --git a/Grinder/ui/task/TaskWidget.ui b/Grinder/ui/task/TaskWidget.ui index 48b80f2..d208c6e 100644 --- a/Grinder/ui/task/TaskWidget.ui +++ b/Grinder/ui/task/TaskWidget.ui @@ -50,7 +50,7 @@ <item row="0" column="0" rowspan="2" colspan="2"> <widget class="QLabel" name="lblTitle"> <property name="sizePolicy"> - <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> @@ -101,7 +101,7 @@ border-radius: 5px;</string> <item row="2" column="0" colspan="2"> <widget class="QLabel" name="lblMessage"> <property name="sizePolicy"> - <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> diff --git a/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.cpp b/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.cpp new file mode 100644 index 0000000..5adc76b --- /dev/null +++ b/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.cpp @@ -0,0 +1,57 @@ +/****************************************************************************** + * File: BaristaCaffeWorkerTaskWidget.cpp + * Date: 20.11.2018 + *****************************************************************************/ + +#include "Grinder.h" +#include "BaristaTrainingTaskWidget.h" +#include "ui_BaristaTrainingTaskWidget.h" +#include "task/tasks/BaristaTrainingTask.h" + +BaristaTrainingTaskWidget::BaristaTrainingTaskWidget(BaristaTrainingTask* task, bool newTask, QWidget *parent) : ConfigureTaskWidget(task, newTask, parent), + ui{new Ui::BaristaTrainingTaskWidget} +{ + setupUi(); +} + +BaristaTrainingTaskWidget::~BaristaTrainingTaskWidget() +{ + delete ui; +} + +void BaristaTrainingTaskWidget::verifySettings() +{ + if (ui->txtLibraryPath->text().isEmpty()) + showError("Please enter a library path.", ui->txtLibraryPath); + + if (ui->txtSessionPath->text().isEmpty()) + showError("Please enter a session path.", ui->txtSessionPath); + + if (ui->txtSolverPath->text().isEmpty()) + showError("Please enter a solver path.", ui->txtSolverPath); +} + +void BaristaTrainingTaskWidget::setupUi() +{ + ui->setupUi(this); +} + +void BaristaTrainingTaskWidget::applySettings(bool save) +{ + if (save) + { + _task->setBaristaPort(ui->txtWorkerPort->value()); + + _task->setLibraryPath(ui->txtLibraryPath->text()); + _task->setSessionPath(ui->txtSessionPath->text()); + _task->setSolverPath(ui->txtSolverPath->text()); + } + else + { + ui->txtWorkerPort->setValue(_task->getBaristaPort()); + + ui->txtLibraryPath->setText(_task->getLibraryPath()); + ui->txtSessionPath->setText(_task->getSessionPath()); + ui->txtSolverPath->setText(_task->getSolverPath()); + } +} diff --git a/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.h b/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.h new file mode 100644 index 0000000..9d29473 --- /dev/null +++ b/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * File: BaristaCaffeWorkerTaskWidget.h + * Date: 20.11.2018 + *****************************************************************************/ + +#ifndef BARISTACAFFEWORKERTASKWIDGET_H +#define BARISTACAFFEWORKERTASKWIDGET_H + +#include "ui/task/ConfigureTaskWidget.h" + +namespace Ui +{ + class BaristaTrainingTaskWidget; +} + +namespace grndr +{ + class BaristaTrainingTask; + + class BaristaTrainingTaskWidget : public ConfigureTaskWidget<BaristaTrainingTask> + { + Q_OBJECT + + public: + BaristaTrainingTaskWidget(BaristaTrainingTask* task, bool newTask, QWidget* parent = nullptr); + virtual ~BaristaTrainingTaskWidget(); + + public: + virtual void verifySettings() override; + virtual void applySettings(bool save) override; + + private: + Ui::BaristaTrainingTaskWidget *ui; + void setupUi(); + }; +} + +#endif diff --git a/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.ui b/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.ui new file mode 100644 index 0000000..6b0700f --- /dev/null +++ b/Grinder/ui/task/tasks/BaristaTrainingTaskWidget.ui @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BaristaTrainingTaskWidget</class> + <widget class="QWidget" name="BaristaTrainingTaskWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>371</width> + <height>116</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Library &path:</string> + </property> + <property name="buddy"> + <cstring>txtLibraryPath</cstring> + </property> + </widget> + </item> + <item row="2" column="1" colspan="2"> + <widget class="QLineEdit" name="txtLibraryPath"/> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="txtWorkerPort"> + <property name="minimum"> + <number>1024</number> + </property> + <property name="maximum"> + <number>65536</number> + </property> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>&Worker port:</string> + </property> + <property name="buddy"> + <cstring>txtWorkerPort</cstring> + </property> + </widget> + </item> + <item row="1" column="0"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>6</height> + </size> + </property> + </spacer> + </item> + <item row="5" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="3" column="1" colspan="2"> + <widget class="QLineEdit" name="txtSessionPath"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Sessio&n path:</string> + </property> + <property name="buddy"> + <cstring>txtSessionPath</cstring> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>&Solver path:</string> + </property> + <property name="buddy"> + <cstring>txtSolverPath</cstring> + </property> + </widget> + </item> + <item row="4" column="1" colspan="2"> + <widget class="QLineEdit" name="txtSolverPath"/> + </item> + </layout> + </widget> + <tabstops> + <tabstop>txtWorkerPort</tabstop> + <tabstop>txtLibraryPath</tabstop> + <tabstop>txtSessionPath</tabstop> + <tabstop>txtSolverPath</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/Grinder/ui/task/tasks/ConfigureGenericTaskWidget.cpp b/Grinder/ui/task/tasks/GenericTaskWidget.cpp similarity index 66% rename from Grinder/ui/task/tasks/ConfigureGenericTaskWidget.cpp rename to Grinder/ui/task/tasks/GenericTaskWidget.cpp index e136d9d..fc3524b 100644 --- a/Grinder/ui/task/tasks/ConfigureGenericTaskWidget.cpp +++ b/Grinder/ui/task/tasks/GenericTaskWidget.cpp @@ -4,34 +4,34 @@ *****************************************************************************/ #include "Grinder.h" -#include "ConfigureGenericTaskWidget.h" -#include "ui_ConfigureGenericTaskWidget.h" +#include "GenericTaskWidget.h" +#include "ui_GenericTaskWidget.h" #include "task/tasks/GenericTask.h" #include "ui/UIUtils.h" -ConfigureGenericTaskWidget::ConfigureGenericTaskWidget(GenericTask* task, bool newTask, QWidget *parent) : ConfigureTaskWidget(task, newTask, parent), - ui{new Ui::ConfigureGenericTaskWidget} +GenericTaskWidget::GenericTaskWidget(GenericTask* task, bool newTask, QWidget *parent) : ConfigureTaskWidget(task, newTask, parent), + ui{new Ui::GenericTaskWidget} { setupUi(); } -ConfigureGenericTaskWidget::~ConfigureGenericTaskWidget() +GenericTaskWidget::~GenericTaskWidget() { delete ui; } -void ConfigureGenericTaskWidget::setupUi() +void GenericTaskWidget::setupUi() { ui->setupUi(this); } -void ConfigureGenericTaskWidget::verifySettings() +void GenericTaskWidget::verifySettings() { if (ui->txtCommand->text().isEmpty()) showError("Please enter a command to execute.", ui->txtCommand); } -void ConfigureGenericTaskWidget::applySettings(bool save) +void GenericTaskWidget::applySettings(bool save) { if (save) { @@ -47,7 +47,7 @@ void ConfigureGenericTaskWidget::applySettings(bool save) } } -void ConfigureGenericTaskWidget::on_btnBrowse_clicked() +void GenericTaskWidget::on_btnBrowse_clicked() { auto command = UIUtils::askFileName(false, "GenericTaskCommand", this, "Select an executable", "All files (*.*)"); diff --git a/Grinder/ui/task/tasks/ConfigureGenericTaskWidget.h b/Grinder/ui/task/tasks/GenericTaskWidget.h similarity index 60% rename from Grinder/ui/task/tasks/ConfigureGenericTaskWidget.h rename to Grinder/ui/task/tasks/GenericTaskWidget.h index 67bc7cd..ca170b1 100644 --- a/Grinder/ui/task/tasks/ConfigureGenericTaskWidget.h +++ b/Grinder/ui/task/tasks/GenericTaskWidget.h @@ -3,27 +3,27 @@ * Date: 07.11.2018 *****************************************************************************/ -#ifndef CONFIGUREGENERICTASKWIDGET_H -#define CONFIGUREGENERICTASKWIDGET_H +#ifndef GENERICTASKWIDGET_H +#define GENERICTASKWIDGET_H #include "ui/task/ConfigureTaskWidget.h" namespace Ui { - class ConfigureGenericTaskWidget; + class GenericTaskWidget; } namespace grndr { class GenericTask; - class ConfigureGenericTaskWidget : public ConfigureTaskWidget<GenericTask> + class GenericTaskWidget : public ConfigureTaskWidget<GenericTask> { Q_OBJECT public: - ConfigureGenericTaskWidget(GenericTask* task, bool newTask, QWidget* parent = nullptr); - ~ConfigureGenericTaskWidget(); + GenericTaskWidget(GenericTask* task, bool newTask, QWidget* parent = nullptr); + virtual ~GenericTaskWidget(); public: virtual void verifySettings() override; @@ -33,7 +33,7 @@ namespace grndr void on_btnBrowse_clicked(); private: - Ui::ConfigureGenericTaskWidget* ui; + Ui::GenericTaskWidget* ui; void setupUi(); }; } diff --git a/Grinder/ui/task/tasks/ConfigureGenericTaskWidget.ui b/Grinder/ui/task/tasks/GenericTaskWidget.ui similarity index 95% rename from Grinder/ui/task/tasks/ConfigureGenericTaskWidget.ui rename to Grinder/ui/task/tasks/GenericTaskWidget.ui index dddd768..57eede4 100644 --- a/Grinder/ui/task/tasks/ConfigureGenericTaskWidget.ui +++ b/Grinder/ui/task/tasks/GenericTaskWidget.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ConfigureGenericTaskWidget</class> - <widget class="QWidget" name="ConfigureGenericTaskWidget"> + <class>GenericTaskWidget</class> + <widget class="QWidget" name="GenericTaskWidget"> <property name="geometry"> <rect> <x>0</x> -- GitLab