From 44ac6e88027247f0c3dde5b099cd71dbf32eaa96 Mon Sep 17 00:00:00 2001 From: Emanuele Date: Mon, 21 Oct 2019 11:37:31 +0200 Subject: [PATCH] Commit Iniziale --- .gitignore | 1 + PiantaRobot_Alto.png | Bin 0 -> 24109 bytes RoboGlue.pro | 80 + RoboGlue.pro.user | 522 + RoboGlue.pro.user.07204ca | 270 + com/RoboGlue_Publisher.cpp | 25 + com/mqtt_callback.cpp | 25 + com/mqtt_callback.h | 23 + com/roboglue_com.cpp | 390 + com/roboglue_com.h | 157 + gui/roboglue_gui.cpp | 343 + gui/roboglue_gui.h | 108 + gui/roboglue_gui.ui | 1323 + init/roboglue_init.cpp | 196 + init/roboglue_init.h | 61 + init/roboglue_init.ui | 279 + libJson/json.hpp | 20406 ++++++++++++++++ libURcom/URCLinterface.cpp | 258 + libURcom/URCLinterface.h | 54 + libURcom/clientMessage.cpp | 455 + libURcom/clientMessage.h | 182 + libURcom/packagetypes.h | 349 + libmodbus-master/.dir-locals.el | 4 + libmodbus-master/.gitignore | 46 + libmodbus-master/.travis.yml | 7 + libmodbus-master/Makefile.am | 42 + libmodbus-master/modbus-data.c | 233 + libmodbus-master/modbus-private.h | 116 + libmodbus-master/modbus-rtu-private.h | 76 + libmodbus-master/modbus-rtu.c | 1299 + libmodbus-master/modbus-rtu.h | 42 + libmodbus-master/modbus-tcp-private.h | 44 + libmodbus-master/modbus-tcp.c | 929 + libmodbus-master/modbus-tcp.h | 52 + libmodbus-master/modbus-version.h | 53 + libmodbus-master/modbus.c | 1909 ++ libmodbus-master/modbus.h | 293 + libmodbus-master/win32/Make-tests | 62 + libmodbus-master/win32/README.win32 | 20 + libmodbus-master/win32/config.h.win32 | 167 + libmodbus-master/win32/configure.js | 163 + libmodbus-master/win32/modbus-9.sln | 20 + libmodbus-master/win32/modbus.dll.manifest.in | 14 + libmodbus-master/win32/modbus.rc | 55 + libmodbus-master/win32/modbus.vcproj | 457 + main.cpp | 25 + main/roboglue_main.cpp | 5 + main/roboglue_main.h | 33 + names.json | 24 + roboglue.conf | 42 + roboglue_comROS.cpp | 0 roboglue_resources.qrc | 5 + shared/roboglue_shared.cpp | 113 + shared/roboglue_shared.h | 129 + track/roboglue_track.cpp | 5 + track/roboglue_track.h | 31 + 56 files changed, 32022 insertions(+) create mode 100644 .gitignore create mode 100644 PiantaRobot_Alto.png create mode 100644 RoboGlue.pro create mode 100644 RoboGlue.pro.user create mode 100644 RoboGlue.pro.user.07204ca create mode 100644 com/RoboGlue_Publisher.cpp create mode 100644 com/mqtt_callback.cpp create mode 100644 com/mqtt_callback.h create mode 100644 com/roboglue_com.cpp create mode 100644 com/roboglue_com.h create mode 100644 gui/roboglue_gui.cpp create mode 100644 gui/roboglue_gui.h create mode 100644 gui/roboglue_gui.ui create mode 100644 init/roboglue_init.cpp create mode 100644 init/roboglue_init.h create mode 100644 init/roboglue_init.ui create mode 100644 libJson/json.hpp create mode 100644 libURcom/URCLinterface.cpp create mode 100644 libURcom/URCLinterface.h create mode 100644 libURcom/clientMessage.cpp create mode 100644 libURcom/clientMessage.h create mode 100644 libURcom/packagetypes.h create mode 100644 libmodbus-master/.dir-locals.el create mode 100644 libmodbus-master/.gitignore create mode 100644 libmodbus-master/.travis.yml create mode 100644 libmodbus-master/Makefile.am create mode 100644 libmodbus-master/modbus-data.c create mode 100644 libmodbus-master/modbus-private.h create mode 100644 libmodbus-master/modbus-rtu-private.h create mode 100644 libmodbus-master/modbus-rtu.c create mode 100644 libmodbus-master/modbus-rtu.h create mode 100644 libmodbus-master/modbus-tcp-private.h create mode 100644 libmodbus-master/modbus-tcp.c create mode 100644 libmodbus-master/modbus-tcp.h create mode 100644 libmodbus-master/modbus-version.h create mode 100644 libmodbus-master/modbus.c create mode 100644 libmodbus-master/modbus.h create mode 100644 libmodbus-master/win32/Make-tests create mode 100644 libmodbus-master/win32/README.win32 create mode 100644 libmodbus-master/win32/config.h.win32 create mode 100644 libmodbus-master/win32/configure.js create mode 100644 libmodbus-master/win32/modbus-9.sln create mode 100644 libmodbus-master/win32/modbus.dll.manifest.in create mode 100644 libmodbus-master/win32/modbus.rc create mode 100644 libmodbus-master/win32/modbus.vcproj create mode 100644 main.cpp create mode 100644 main/roboglue_main.cpp create mode 100644 main/roboglue_main.h create mode 100644 names.json create mode 100644 roboglue.conf create mode 100644 roboglue_comROS.cpp create mode 100644 roboglue_resources.qrc create mode 100644 shared/roboglue_shared.cpp create mode 100644 shared/roboglue_shared.h create mode 100644 track/roboglue_track.cpp create mode 100644 track/roboglue_track.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8fe4fa --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.project diff --git a/PiantaRobot_Alto.png b/PiantaRobot_Alto.png new file mode 100644 index 0000000000000000000000000000000000000000..66bb22a7d060ec7683fa3ea75c0b12ec5be209b7 GIT binary patch literal 24109 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 literal 0 HcmV?d00001 diff --git a/RoboGlue.pro b/RoboGlue.pro new file mode 100644 index 0000000..e446cee --- /dev/null +++ b/RoboGlue.pro @@ -0,0 +1,80 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2019-01-21T13:30:50 +# +#------------------------------------------------- + +QT += core \ + gui \ + network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = RoboGlue +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +CONFIG += c++11 +QMAKE_CXXFLAGS += -Wno-unused-parameter \ + -Wno-unused-variable + +SOURCES += \ + main.cpp \ + libURcom/clientMessage.cpp \ + libURcom/URCLinterface.cpp \ + init/roboglue_init.cpp \ + com/roboglue_com.cpp \ + gui/roboglue_gui.cpp \ + main/roboglue_main.cpp \ + shared/roboglue_shared.cpp \ + track/roboglue_track.cpp \ + com/mqtt_callback.cpp + +HEADERS += \ + libURcom/clientMessage.h \ + libURcom/packagetypes.h \ + libURcom/URCLinterface.h \ + init/roboglue_init.h \ + com/roboglue_com.h \ + gui/roboglue_gui.h \ + main/roboglue_main.h \ + shared/roboglue_shared.h \ + track/roboglue_track.h \ + libJson/json.hpp \ + com/mqtt_callback.h + +FORMS += \ + init/roboglue_init.ui \ + gui/roboglue_gui.ui + + +LIBS += -lboost_system \ + -lboost_thread \ + -lboost_chrono \ + -lpthread \ + -lpaho-mqttpp3 \ + -lpaho-mqtt3a + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +DISTFILES += \ + + +SUBDIRS += \ + RoboGlue.pro + +RESOURCES += \ + roboglue_resources.qrc diff --git a/RoboGlue.pro.user b/RoboGlue.pro.user new file mode 100644 index 0000000..edd25e6 --- /dev/null +++ b/RoboGlue.pro.user @@ -0,0 +1,522 @@ + + + + + + EnvironmentId + {3d769059-8219-4d9e-8059-e25a3e815e0e} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + qt + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + true + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.12.1 GCC 64bit + Desktop Qt 5.12.1 GCC 64bit + qt.qt5.5121.gcc_64_kit + 0 + 0 + 0 + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop_Qt_5_12_0_GCC_64bit-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop_Qt_5_12_1_GCC_64bit-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop_Qt_5_12_1_GCC_64bit-Profile + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 3 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy Configuration + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + RoboGlue + + Qt4ProjectManager.Qt4RunConfiguration:/home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/RoboGlue/RoboGlue.pro + RoboGlue.pro + + 3768 + false + true + true + false + false + true + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop_Qt_5_12_0_GCC_64bit-Debug + + 1 + + + + ProjectExplorer.Project.Target.1 + + Desktop + Desktop + {b917fd33-cfca-4cf9-a8d9-025510846768} + 0 + 0 + 0 + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop-Release + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 2 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy Configuration + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + RoboGlue + + Qt4ProjectManager.Qt4RunConfiguration:/home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/RoboGlue/RoboGlue.pro + RoboGlue.pro + + 3768 + false + true + true + false + false + true + + + + 1 + + + + ProjectExplorer.Project.TargetCount + 2 + + + ProjectExplorer.Project.Updater.FileVersion + 20 + + + Version + 20 + + diff --git a/RoboGlue.pro.user.07204ca b/RoboGlue.pro.user.07204ca new file mode 100644 index 0000000..c241e73 --- /dev/null +++ b/RoboGlue.pro.user.07204ca @@ -0,0 +1,270 @@ + + + + + + EnvironmentId + {07204ca4-3ee6-4fe3-8a4e-4be58efc8824} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + qt + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + true + + + + ProjectExplorer.Project.Target.0 + + Qt 5.12.0 (gcc_64) + Qt 5.12.0 (gcc_64) + {a7ccd2d8-4d86-488f-8674-e3cd54db0525} + 1 + 0 + 0 + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Qt_5_12_0_gcc_64_temporary-Profile + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + true + true + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Profile + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop_Qt_5_12_0_GCC_64bit-Debug + + + true + qmake + + QtProjectManager.QMakeBuildStep + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + false + + 2 + Build + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + false + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + true + + 2 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy Configuration + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 2 + true + true + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + RoboGlue + + Qt4ProjectManager.Qt4RunConfiguration:/home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/RoboGlue/RoboGlue.pro + RoboGlue.pro + + 3768 + false + true + true + false + false + true + + /home/emanuele/Documents/GestioneMacchine/Robot_Incollaggio/Software/QtCreator-Workspace/build-RoboGlue-Desktop_Qt_5_12_0_GCC_64bit-Debug + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 20 + + + Version + 20 + + diff --git a/com/RoboGlue_Publisher.cpp b/com/RoboGlue_Publisher.cpp new file mode 100644 index 0000000..9caf0af --- /dev/null +++ b/com/RoboGlue_Publisher.cpp @@ -0,0 +1,25 @@ +#include "ros/ros.h" +#include "std_msgs/String.h" + +int main(int argc, char **argv) +{ + ros::init(argc, argv, "RoboGlue_Publisher"); + ros::NodeHandle nh; + + ros::Publisher chatter_pub = nh.advertise("chatter", 1000); + + ros::Rate loop_rate(10); + while (ros::ok()) + { + std_msgs::String msg; + msg.data = "hello world"; + + chatter_pub.publish(msg); + + ros::spinOnce(); + + loop_rate.sleep(); + } + + return 0; +} diff --git a/com/mqtt_callback.cpp b/com/mqtt_callback.cpp new file mode 100644 index 0000000..a328759 --- /dev/null +++ b/com/mqtt_callback.cpp @@ -0,0 +1,25 @@ +#include "mqtt_callback.h" + +mqtt_callback::mqtt_callback(mqtt::async_client *cli, std::shared_ptr log) { + //initialize client pointer + cli_=cli; + log_=log; +} + +void mqtt_callback::connected(const mqtt::string &cause) { + log_->info("MQTT client Connected: {}", cause.c_str()); +} + +void mqtt_callback::connection_lost(const mqtt::string &cause) { + log_->info("MQTT client Disconnected {}", cause.c_str()); +} + +void mqtt_callback::message_arrived(mqtt::const_message_ptr msg) { + log_->debug("Message Arrived: topic->{} - message->{}", + msg->get_topic(), msg->to_string()); +} + +void mqtt_callback::delivery_complete(mqtt::delivery_token_ptr tok) { + log_->trace("Message Delivery complete \n tok: {}", tok->get_message_id()); +} + diff --git a/com/mqtt_callback.h b/com/mqtt_callback.h new file mode 100644 index 0000000..e54bd80 --- /dev/null +++ b/com/mqtt_callback.h @@ -0,0 +1,23 @@ +#ifndef MQTT_CALLBACK_H +#define MQTT_CALLBACK_H + +#include +#include +#include + +class mqtt_callback : public virtual mqtt::callback { + +private: + mqtt::async_client *cli_ = nullptr; + std::shared_ptr log_; + +public: + mqtt_callback(mqtt::async_client *cli, std::shared_ptr log); + + void connected(const mqtt::string &cause) override; + void connection_lost(const mqtt::string &cause) override; + void message_arrived(mqtt::const_message_ptr msg) override; + void delivery_complete(mqtt::delivery_token_ptr tok) override; +}; + +#endif diff --git a/com/roboglue_com.cpp b/com/roboglue_com.cpp new file mode 100644 index 0000000..3a8242a --- /dev/null +++ b/com/roboglue_com.cpp @@ -0,0 +1,390 @@ +#include "roboglue_com.h" + +RoboGlue_COM::RoboGlue_COM (RoboGlue_SHARED *mem): m(mem) { + + //////// SETUP LOGGER ////////// + liblog = spdlog::stdout_logger_mt("URcom_liblog"); + modlog = spdlog::stdout_logger_mt("RoboGlue_comlog"); + liblog->set_level(spdlog::level::info); + modlog->set_level(spdlog::level::debug); + + ///////// INITIALIZE TCP SERVER /////////// + server = new QTcpServer(this); + server->listen(QHostAddress::Any, 9000); + connect(server, SIGNAL(newConnection()), this, SLOT(on_internalRobotConnect())); + modlog->info("COM started"); + + //////// INITIALIZE MQTT CONNECTOR /////// + client = new mqtt::async_client(std::string("tcp://localhost:1883"),""); + callback = new mqtt_callback(client,modlog);//////////////////////////////////////////////// + //////// END EXTERNAL PUBLIC SLOTS ///////////// + //////////////////////////////////////////////// + baseMsg = m->commonSettings.baseMessage; + baseMsg["command"]=nullptr; + baseMsg["params"]=nullptr; + + //////// INITIALIZE TIMED CALLBACK FUNCTIONS ////// + coordSendTimer = new QTimer(this); + connect(coordSendTimer, SIGNAL(timeout()), this, SLOT(timed_sendCoordinates())); +} + +void RoboGlue_COM::run() { + unsigned char attempt = 0; + + /// START MQTT CLIENT //// + client->set_callback(*callback); + client->connect()->wait(); + // subscrbe to mqtt topics + std::vector subList = m->comSettings.subList; + for (uint i=0; i < subList.size(); i++){ + client->subscribe(subList[i],0); + } + client->start_consuming(); + + /// AUTOCONNECT ROBOT INTERFACE /// + if(m->comSettings.connection.autoConnect) this->on_connect(QString(m->comSettings.connection.robotIp.c_str()), + m->comSettings.connection.robotPort, + m->comSettings.connection.retry); + /// THREAD LOOP /// + while(this->isRunning){ + // if host is not connected and the reader thread is stopped do nothing + if (!this->isConnected & !this->isReading) { + msleep(100); + } + // if host is connected but reader thread is not started, start it + else if (this->isConnected & !this->isReading) { + this->isConnected=false; + // create new robot interface + interface = new UR_CLinterface(liblog); + // try connect to robot + while (!this->isConnected & (attempt++ < m->comSettings.connection.retry)){ + attempt = 0; + modlog->debug("Connection Attempt:{0}",attempt); + try { + this->isConnected = interface->connect(m->comSettings.connection.robotIp.c_str(), + m->comSettings.connection.robotPort); + } catch (boost::system::system_error &e){ + modlog->error("Connection Failed:\n {}", e.what()); + } + } + if (this->isConnected & ! this->isReading) { + // create new reader thread and connect new data signal + modlog->info("Interface Connected"); + readerThr = new readerThread(&this->isReading, interface, &localData); + // connect readerThread newdata signal + connect(readerThr, SIGNAL(newDataAvailable()), this, SLOT(on_internalNewData())); + readerThr->start(); + modlog->debug("ReaderThread is Working"); + this->isReading = true; + } else modlog->error("Connection failed"); + } + // if host is connected and reader is running, do nothing + else if (this->isConnected & this->isReading) { + if (robotConnected){ + if(robotIN->bytesAvailable()){ + robotRequest = robotIN->readAll().toStdString(); + robotRequest.pop_back(); + modlog->info("robotMessage {}", robotRequest.c_str()); + processRobotRequest(robotRequest); + } + } + emit com_heartBeat(); + msleep(10); + } + // if host is to be disconnected but still reading, stop reading and disconnect host + else if (!this->isConnected & this->isReading){ + // disable reading, should stop reader thread, disconnect new data signal + disconnect(readerThr, SIGNAL(newDataAvailable()), this, SLOT(on_internalNewData())); + this->isReading = false; + readerThr->wait(); + modlog->debug("ReaderThread is Stopped"); + // wait until thread exits + this->isConnected=false; + interface->disconnect(); + modlog->info("Interface Disconnected"); + // schedule readerThread for deletion; + readerThr->deleteLater(); + // delete and rebuild interface for next connection; + delete interface; + interface = nullptr; + } + } + if (isConnected) { + robotIN->disconnectFromHost(); + robotIN->deleteLater(); + server->deleteLater(); + on_disconnect(); + readerThr->wait(); + coordSendTimer->stop(); + } + if (interface != nullptr) delete interface; + if (coordSendTimer != nullptr) delete coordSendTimer; + spdlog::drop("URcom_liblog"); + spdlog::drop("RoboGlue_comlog"); +} + + +///////////////////////////////////////////////// +//////////////////// READER THREAD ////////////// +//////////////////////////////////////////////// +void readerThread::run(){ + this->loopRead(); +} +void readerThread::loopRead() { + while(*this->isReading){ + interface->readData(); + interface->parseData(); + *dataOut=interface->clientData; + // emit signal every new data received + emit newDataAvailable(); + } +} +///////////////////////////////////////////////// +///////////////END READER THREAD//////////////// +//////////////////////////////////////////////// + +//////////////////////////////////////////////// +/////////////// MQTT FUNCTIONS ///////////////// +//////////////////////////////////////////////// +void RoboGlue_COM::MQTTpublish(json msg, string topic) { + std::string msgstr = msg.dump(); + mqtt::token_ptr tok = client->publish(mqtt::make_message(topic,msgstr)); + modlog->trace("Published: id:{0}, topic:{1}, msg:{2}", + tok->get_message_id(), tok->get_topics(), msgstr); +} +//////////////////////////////////////////////// +////////////END MQTT FUNCTIONS ///////////////// +//////////////////////////////////////////////// + +void RoboGlue_COM::saveKinematicInfo() { + m->mutex.lock(); + m->settings->beginGroup("robot"); + m->settings->beginGroup("kine"); + m->settings->beginWriteArray("dhtable"); + for (int i=0; isettings->setArrayIndex(i); + m->settings->setValue("dha", localData.configuration.kinematics.DHa[i]); + m->settings->setValue("dhd", localData.configuration.kinematics.DHd[i]); + m->settings->setValue("dhalpha", localData.configuration.kinematics.DHalpha[i]); + m->settings->setValue("dhtheta", localData.configuration.kinematics.DHtheta[i]); + } + m->settings->endArray(); + m->settings->endGroup(); + m->settings->endGroup(); + m->mutex.unlock(); +} + +void RoboGlue_COM::saveConnectionInfo() { + m->mutex.lock(); + m->settings->beginGroup("robot"); + m->settings->beginGroup("connection"); + m->settings->setValue("robotip",QString(m->comSettings.connection.robotIp.c_str())); + m->settings->setValue("robotport",m->comSettings.connection.robotPort); + m->settings->setValue("robotretry",m->comSettings.connection.retry); + m->settings->setValue("autoconnect",m->comSettings.connection.autoConnect); + m->settings->endGroup(); + m->settings->endGroup(); + m->mutex.unlock(); +} + +void RoboGlue_COM::processRobotRequest(std::string request) { + std::string response; + std::string varName; + std::string varValue; + std::vector tokes; + + m->mutex.lock(); + // split command into tokes + boost::algorithm::split(tokes,request,boost::is_any_of(" ")); + // process requested action + if (tokes[COMMAND] == "GET"){ + modlog->debug("Robot GET Request"); + varName = tokes[VARNAME]; + response = varName + " "; + try { + response += std::to_string(m->robotVariables.at(varName)); + } catch (std::out_of_range &e) { + modlog->error("{0}:{1}",e.what(), varName); + } + response.append("\n"); + } else + if (tokes[COMMAND] == "SET") { + modlog->debug("Robot SET Request"); + varName = tokes[VARNAME]; + varValue = tokes[VALUE]; + try { + // if variable does not exist insert it into map + m->robotVariables[varName] = std::stoi(varValue.c_str()); + } catch (std::out_of_range &e) { + modlog->error("{0}:{1}",e.what(), varName); + } + } else { + modlog->debug("Robot GENERAL Request"); + response = "generalrequest"; + response.insert(0,&m->comSettings.connection.prefix); + response.append(&m->comSettings.connection.suffix); + } + m->mutex.unlock(); + robotIN->write(response.c_str()); + robotIN->flush(); +} + +//////////////////////////////////////////////// +////////////INTERNAL PRIVATE SLOTS////////////// +//////////////////////////////////////////////// +void RoboGlue_COM::on_internalRobotConnect() { + if(robotIN != nullptr) { + robotConnected = false; + delete robotIN; + } + modlog->info("Robot request connected"); + robotIN = server->nextPendingConnection(); + robotConnected = true; +} + +void RoboGlue_COM::on_internalNewData(){ + this->m->mutex.lock(); + m->robotData=this->localData; + this->m->mutex.unlock(); + emit com_newData(); + // save DH matrix data; + if(isFirstMessage) { + this->saveKinematicInfo(); + isFirstMessage = false; + } +} + +void RoboGlue_COM::timed_sendCoordinates(){ + QList sp = this->lastCoord; + if (this->isCoordNew) + this->isCoordNew = false; + if (!sp.empty()){ + modlog->trace("timedSendCoordinate"); + baseMsg.clear(); + baseMsg["type"] = "cartesian"; + baseMsg["mode"] = "pos"; + baseMsg["value"] = json(); + baseMsg["value"]["x"] = sp[0]; + baseMsg["value"]["y"] = sp[1]; + baseMsg["value"]["z"] = sp[2]; + baseMsg["value"]["rx"] = sp[3]; + baseMsg["value"]["ry"] = sp[4]; + baseMsg["value"]["rz"] = sp[5]; + MQTTpublish(baseMsg, m->comSettings.pubList[m->comSettings.COORD]); + } +} +//////////////////////////////////////////////// +////////END INTERNAL PRIVATE SLOTS////////////// +//////////////////////////////////////////////// + +void RoboGlue_COM::on_commonStatusChange() { + if (m->commonStatus.isRecording || m->commonStatus.isRealtime) { + if (!coordSendTimer->isActive()) coordSendTimer->start(100); + } + if (!m->commonStatus.isRecording && !m->commonStatus.isRealtime){ + if (coordSendTimer->isActive()) coordSendTimer->stop(); + } +} + +void RoboGlue_COM::on_quit() { + this->isConnected=false; + client->disconnect(); + while(this->isReading) msleep(100); + this->isRunning = false; +} + +void RoboGlue_COM::on_connect(QString ip, uint port, uchar retry) { + modlog->debug("robotConnect Received"); + if (!isConnected){ + this->isConnected=true; + this->isReading=false; + this->isFirstMessage=true; + // update connection information for the thread + m->comSettings.connection.robotIp = ip.toStdString(); + m->comSettings.connection.robotPort = port; + m->comSettings.connection.retry = retry; + // save connection info + saveConnectionInfo(); + } +} + +void RoboGlue_COM::on_disconnect() { + modlog->debug("robotDisconnect Received"); + if(isConnected){ + this->isConnected = false; + } +} + +void RoboGlue_COM::on_sendURscript(QString command) { + modlog->debug("robotSendURscript Received"); + if(isConnected){ + //command sent directly trough interface, NOT ROS + this->interface->sendCommand(command.toStdString()); + } +} + +void RoboGlue_COM::on_sendROScommand(QString command, QVariantMap params) { + modlog->debug("robotSendCommand Received"); + baseMsg.clear(); + baseMsg["command"] = command.toStdString(); + QJsonDocument jparam = QJsonDocument(QJsonObject::fromVariantMap(params)); + baseMsg["params"] = json::parse(jparam.toJson().data()); + MQTTpublish(baseMsg, m->comSettings.pubList[m->comSettings.COMM]); +} + +void RoboGlue_COM::on_sendROScoordinates(QList sp) { + this->lastCoord.clear(); + this->lastCoord = sp; + this->isCoordNew = true; +} + +void RoboGlue_COM::on_sendROSstate(QMap digital, QMap analog){ + modlog->debug("robotSendState Received"); + baseMsg.clear(); + baseMsg["digital"] = nlohmann::json(); + baseMsg["analog"] = nlohmann::json(); + for (auto di : digital.keys()) { + baseMsg["digital"][di] = digital[di]; + } +} +//////////////////////////////////////////////// +//////// END EXTERNAL PUBLIC SLOTS ///////////// +//////////////////////////////////////////////// +//void RoboGlue_COM::on_sendSetpointOLD(QList sp) { +// std::string setpoint; +// struct { +// union{ +// struct{ +// uint32_t x,y,z,a,b,c,keepalive; +// }; +// char bytes[28]; +// }; +// } setPointBytes; +// setPointBytes.keepalive=1; +// int32_t v; +// setpoint = "("; +// for(uchar i=0; i(sp[i]*1000); +// if (i>2) v=boost::algorithm::clamp(v, -2680,2680); +// setpoint.append(std::to_string(v)); +// boost::algorithm::replace_all(setpoint, ",","."); +// if(i(sp[0]*1000)); +// setPointBytes.y=htonl(static_cast(sp[1]*1000)); +// setPointBytes.z=htonl(static_cast(sp[2]*1000)); +// setPointBytes.a=htonl(static_cast(sp[3]*1000)); +// setPointBytes.b=htonl(static_cast(sp[4]*1000)); +// setPointBytes.c=htonl(static_cast(sp[5]*1000)); +// setPointBytes.keepalive=htonl(setPointBytes.keepalive); + +// if(robotIN->write(setPointBytes.bytes,28)) +// modlog->trace("Sent Setpoint {}",setpoint.c_str()); +// robotIN->flush(); +//} + + diff --git a/com/roboglue_com.h b/com/roboglue_com.h new file mode 100644 index 0000000..7735997 --- /dev/null +++ b/com/roboglue_com.h @@ -0,0 +1,157 @@ +#ifndef ROBOGLUE_COM_H +#define ROBOGLUE_COM_H + +/* + * roboglue_com.cpp + * + * Created on: Jan 21, 2019 + * Author: emanuele + * + * Modulo di comunicazione del software roboglue, è un thread che continua a leggere le ibformazioni del robot + * e le scrive nella memoria condivisa tra i moduli, rivece comandi sotto forma di segnali per inviare + * comandi URscript al controller + * + * 20190121 - Prima Scittura, incorpora un thread che si occupa solo della lettura dati, + * ad ora non finzionante. + * 20190122 - Il thread di lettura funziona correttamente ed è implementazione di QThread, + * ad ogni nuovo pacchetto letto viene emess il segnale newData(); + * la classe readerThread contiene solo la funzione che viene spostata nel thread + * di lettura. + * Impostata la struttura comune a tutti i moduli. + * 20190123 - Impostati i segnalli per connessione e disconnessione comandata da GUI. + * 20190125 - Corretti alcuni bug che facevano crashare in caso di mancata connessione. + * Aggiunto un parametero nella configurazione che definisce il massimo raggio di reach + * del robot. Sarà sostituito da una tabella completa delle lunghezze dei bracci per + * il calcolo della cinematica inversa nel main. + * 20190128 - Inizio scrittura modulo tcpsocket per lo scambio dei consensi. + * 20190129 - Continua scrittura del modulo socket, le variabili di scambio con il robot sono + * in una mappa per facilitaà di accesso + * (rif documenti URscript get_var(), set_var()) + * 20190131 - Modifiche alla parte di comunicazione per usare i comandi servoj del robot come + * suggerito nelle librerie di ROS industrial. + * Ricerche iniziate su come usare MoveIT e ros indusrtial con il driver UR per + * la pianificazione e l'invio delle traiettorie. Il problema principale è che + * il driver per UR di ROS è datato e non funziona con il controller versione 5.2 + * ROS indusrtial inoltre non è ancora compatibile con l'ultima versione di ROS + * e ubuntu 18.04. Altervativa valida porebbe essere usare solo MoveIT, vedrò. + * 20190208 - Implementazione della comunicazione ai moduli ros con mqtt + * 20190211 - Continua implementazione della comunicazione ai moduli ros (non ancora scritto) + * con mqtt. Tra Qt e ROS ci saranno 3 code "bidirezionali": COMANDO, COORDINATE, STATO + * per separare i flussi dei messaggi. + * I messaggi sono strutturati come json, fare riferimento al file roboglue_messaggi.txt + * per la specifica di formato. + * I seganli che arrivano portando i messaggi sono strutturati diversamente a seconda + * della coda di destinazione. + * 20190311 - La pubblicazione dei messaggi di coordinate verso ros deve essere a rateo costante + * indipendentemente dalla frequenza di ingresso + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mqtt_callback.h" +#include +#include + +#define COMMAND 0 +#define VARNAME 1 +#define VALUE 2 + +class RoboGlue_COM: public QThread { + Q_OBJECT + + public: + RoboGlue_COM(RoboGlue_SHARED* mem = nullptr); + void run() override; + + private: + shared_ptr liblog; + shared_ptr modlog; + + RoboGlue_SHARED *m; + clientData_t localData; + QThread *readerThr = nullptr; + QTcpServer *server = nullptr; + QTcpSocket *robotIN = nullptr; + mqtt_callback *callback = nullptr; + mqtt::async_client* client = nullptr; + json baseMsg; + std::string robotRequest; + UR_CLinterface *interface = nullptr; + + QTimer* coordSendTimer = nullptr; + QList lastCoord; + bool isCoordNew = false; + + bool isRunning = true; + bool isConnected = false; + bool isReading = false; + bool isNew = false; + bool isFirstMessage = true; + bool robotConnected = false; + + void saveKinematicInfo(); + void saveConnectionInfo(); + void processRobotRequest(std::string); + + void MQTTpublish(json msg, std::string topic); + + signals: + void com_newData(); + void com_heartBeat(); + void com_newROScommand(QString, QVariantMap); + + private slots: + void on_internalRobotConnect(); + void on_internalNewData(); + void timed_sendCoordinates(); + + public slots: + void on_quit(); + void on_commonStatusChange(void); + void on_connect(QString ip = "", uint port = 0, uchar retry = 1); + void on_disconnect(); + void on_sendURscript(QString command); + void on_sendROScommand(QString, QVariantMap); + void on_sendROScoordinates(QList sp); + void on_sendROSstate(QMap digital, QMap analog); + + //void on_sendSetpointOLD(QList sp); +}; + + +class readerThread: public QThread{ + Q_OBJECT + +public: + readerThread(bool* r, UR_CLinterface* i, clientData_t* d){ + this->isReading=r; + this->interface=i; + this->dataOut=d; + } + bool* isReading; + UR_CLinterface* interface; + clientData_t* dataOut; + + void run() override; + void loopRead(); + +signals: + void newDataAvailable(); +}; + +#endif // ROBOGLUE_COM_H diff --git a/gui/roboglue_gui.cpp b/gui/roboglue_gui.cpp new file mode 100644 index 0000000..6faca23 --- /dev/null +++ b/gui/roboglue_gui.cpp @@ -0,0 +1,343 @@ +#include "roboglue_gui.h" +#include "ui_roboglue_gui.h" + +RoboGlue_GUI::RoboGlue_GUI(RoboGlue_SHARED *mem) : m(mem) { + + //////// SETUP LOGGER ////////// + modlog = spdlog::stdout_logger_mt("RoboGlue_guilog"); + spdlog::set_level(spdlog::level::debug); + modlog->info("GUI Started"); + + //////// SETUP USER INTERFACE /////// + ui=new Ui::RoboGlue_GUI; + ui->setupUi(this); + ui->frm_move->setStyleSheet("border-image: url(:/images/PiantaRobot_Alto.png) 0 0 0 0 stretch stretch"); + + //////// RESTORE UI STATE //////////// + ui->txt_roboIp->setText(QString(m->comSettings.connection.robotIp.c_str())); + ui->txt_robotPort->setText(QString::number(m->comSettings.connection.robotPort)); + ui->chk_autoConnect->setChecked(m->comSettings.connection.autoConnect); + + //////// ENABLE HOVERING EVENT OVER DEFINED AREA ////////////// + ui->frm_move->installEventFilter(this); + + //////// CONNECT SIGNALS & SLOTS /////// + connect(this, SIGNAL(destroyed()), + this, SLOT(deleteLater()), Qt::ConnectionType::QueuedConnection); + connect(this, SIGNAL(pad_hoverEvent(QEvent *)), + this, SLOT(on_pad_hoverEvent(QEvent *))); + + /////// READ NAME DEFINITION JSON FILE ////////// + try { + QFile fp(QString(m->guiSettings.namePath.c_str())); + fp.open(QFile::ReadOnly | QFile::Text); + QByteArray rawdata= fp.readAll(); + std::string strdata= rawdata.toStdString(); + nameDefinitions = json::parse(strdata.c_str()); + fp.close(); + } catch (json::exception e){ + modlog->error("Unable to parse name definitions:\n\t{}",e.what()); + } +} + +RoboGlue_GUI::~RoboGlue_GUI() { + spdlog::drop("RoboGlue_guilog"); + delete ui; +} + +bool RoboGlue_GUI::eventFilter(QObject *obj, QEvent *event){ + if(obj == static_cast(ui->frm_move)) + if(event->type()==event->MouseMove || event->type()==event->MouseButtonPress + || event->type()==event->KeyPress){ + event->accept(); + emit pad_hoverEvent(event); + } + return QWidget::eventFilter(obj, event); +} + +QVariantMap RoboGlue_GUI::getLockAxes() { + QVariantMap param; + param["lx"]=ui->chk_lockX->isChecked(); + param["ly"]=ui->chK_lockY->isChecked(); + param["lz"]=ui->chk_lockZ->isChecked(); + param["lrx"]=ui->chk_lockRX->isChecked(); + param["lry"]=ui->chk_lockRY->isChecked(); + param["lrz"]=ui->chk_lockRZ->isChecked(); + return param; +} + +void RoboGlue_GUI::enableLockAxes() { + ui->chk_lockX->setEnabled(true); + ui->chK_lockY->setEnabled(true); + ui->chk_lockZ->setEnabled(true); + ui->chk_lockRX->setEnabled(true); + ui->chk_lockRY->setEnabled(true); + ui->chk_lockRZ->setEnabled(true); +} + +void RoboGlue_GUI::disableLockAxes(){ + ui->chk_lockX->setEnabled(false); + ui->chK_lockY->setEnabled(false); + ui->chk_lockZ->setEnabled(false); + ui->chk_lockRX->setEnabled(false); + ui->chk_lockRY->setEnabled(false); + ui->chk_lockRZ->setEnabled(false); +} + +//////////////////////////////////////////////// +////////////INTERNAL PRIVATE SLOTS////////////// +//////////////////////////////////////////////// +void RoboGlue_GUI::on_commonStatusChange() { + modlog->trace("on_commonStatusChange Received"); +} + +void RoboGlue_GUI::on_pad_hoverEvent(QEvent* e) { + QMouseEvent *mouse = static_cast(e); + QKeyEvent *key = static_cast(e); + static float zpos=0.5; + static bool trackEnable = false; + static QPointF lastPos; + QPointF p; + float limit = m->comSettings.kine.maxReach; + + modlog->trace("event: {}",e->type()); + if(e->type() == e->MouseMove){ + QPointF pos = mouse->localPos(); + qreal x = boost::algorithm::clamp(pos.x(),0,ui->frm_move->size().width()); + qreal y = boost::algorithm::clamp(pos.y(),0,ui->frm_move->size().height()); + p = QPointF(x,y); + } else if(e->type() == e->MouseButtonPress && + (m->commonStatus.isRecording || m->commonStatus.isRealtime)) { + modlog->trace("mousebuttonevent {}", mouse->button()); + if(!trackEnable){ + trackEnable=true; + //mouse->setLocalPos(lastPos); + ui->frm_move->setFocus(); + if (ui->btn_record->isEnabled()) ui->btn_record->setEnabled(false); + if (ui->btn_realtime->isEnabled()) ui->btn_realtime->setEnabled(false); + } else if(trackEnable){ + trackEnable=false; + lastPos=mouse->pos(); + if (m->commonStatus.isRecording) ui->btn_record->setEnabled(true); + if (m->commonStatus.isRealtime) ui->btn_realtime->setEnabled(true); + } + } else if (e->type()==e->KeyPress) { + modlog->trace("keyevent {}", key->key()); + if(key->key() == 16777235) + if (zpos < limit) zpos=zpos+0.1; + if(key->key() == 16777237) + if (zpos > 0) zpos=zpos-0.1; + } + + if(trackEnable && (m->robotVariables.at("RDY") == 1)) { + QTransform t=QTransform::fromTranslate(-ui->frm_move->size().width()/2, + -ui->frm_move->size().height()/2); + t = t * QTransform::fromScale(-limit/t.dx(), + -limit/t.dy()); + QPointF q = t.map(p); + ui->lbl_posX->setText(QString("X: %1m").arg(q.x(),6)); + ui->lbl_posY->setText(QString("Y: %1m").arg(-q.y(),6)); + ui->lbl_posZ->setText(QString("Z: %1m").arg(zpos,6)); + + // FIXME + /////////////////////////////////////////////////////// + ////////////////// TEMPORARY SEND COORDINATES //////// + ////////////////// TO ROBOT ///////////////////////// + /// (TO BE IMPLEMENTED IN MAIN FOR KINEMATICKS CHECK) + if (q.x()>-limit && q.x()-limit && q.y() 0 && zpos < limit){ + QList sp = {q.x(),-q.y(),zpos,1.57,-2.68,0}; + emit sendROScoordinates(sp); + } + } +} + +void RoboGlue_GUI::on_btn_sendCommand_clicked(){ + modlog->debug("robotSendCommand Requested"); + emit robotSendURscript(ui->txt_commandToSend->text()); +} + + +void RoboGlue_GUI::on_btn_robotConnect_clicked() { + modlog->debug("robotConnect Requested"); + emit robotConnect(ui->txt_roboIp->text(), + ui->txt_robotPort->text().toUInt(), + 5); // default retry attempt to 5 +} + +void RoboGlue_GUI::on_btn_robotDisconnect_clicked() { + modlog->debug("robotDisconnect Requested"); + emit robotDisconnect(); +} + +void RoboGlue_GUI::on_chk_autoConnect_clicked(bool checked) { + m->comSettings.connection.autoConnect = checked; + m->settings->beginGroup("robot"); + m->settings->beginGroup("connection"); + m->settings->setValue("autoconnect", checked); + m->settings->endGroup(); + m->settings->endGroup(); +} + +void RoboGlue_GUI::on_btn_record_clicked() { + static bool recording = false; + QVariantMap param; + QVariantMap metadata; + modlog->debug("robotTrajectory Record"); + param.insert("action", ""); + if (!recording) { + m->commonStatus.isRecording = true; + recording = true; + param["action"] = "start"; + param["lock"] = getLockAxes(); + metadata["name"] = ui->txt_fileName->text(); + metadata["code"] = QUuid::createUuid().toString(); + metadata["ds"] = ui->spn_ds->value(); + metadata["dt"] = ui->spn_dt->value(); + param["metadata"] = metadata; + ui->btn_play->setEnabled(false); + ui->btn_realtime->setEnabled(false); + disableLockAxes(); + } else { + m->commonStatus.isRecording = false; + recording = false; + param["action"]="stop"; + ui->btn_play->setEnabled(true); + ui->btn_realtime->setEnabled(true); + enableLockAxes(); + } + emit m->commonStatusChange(); + emit sendROScommand("RECORD", param); +} + +void RoboGlue_GUI::on_btn_play_clicked() { + static bool playing = false; + QVariantMap param; + QVariantMap metadata; + modlog->debug("robotTrajectory Replay"); + if (!playing) { + m->commonStatus.isPlaying = true; + playing = true; + metadata["name"]=ui->txt_fileName->text(); + param["action"]="start"; + param["metadata"] = metadata; + param["lock"] = getLockAxes(); + ui->btn_record->setEnabled(false); + ui->btn_realtime->setEnabled(false); + ui->btn_play->setText("Stop"); + ui->txt_fileName->setEnabled(false); + disableLockAxes(); + } else { + m->commonStatus.isPlaying = false; + playing = false; + param["action"] = "stop"; + ui->btn_record->setEnabled(true); + ui->btn_realtime->setEnabled(true); + ui->btn_play->setText("Play"); + ui->txt_fileName->setEnabled(true); + enableLockAxes(); + } + emit m->commonStatusChange(); + emit sendROScommand("PLAY", param); +} + +void RoboGlue_GUI::on_btn_open_clicked() { + static bool fileOpen = false; + QVariantMap param; + QVariantMap metadata; + modlog->debug("robotTragectory Open"); + if(!fileOpen){ + m->commonStatus.isFileOpen = true; + fileOpen = true; + metadata["name"] = ui->txt_fileName->text(); + metadata["plot"] = true; + param["action"] = "open"; + param["metadata"] = metadata; + ui->btn_record->setEnabled(false); + ui->btn_realtime->setEnabled(false); + ui->btn_open->setText("Close"); + ui->txt_fileName->setEnabled(false); + } else { + m->commonStatus.isFileOpen = false; + fileOpen = false; + param["action"] = "close"; + ui->btn_record->setEnabled(true); + ui->btn_realtime->setEnabled(true); + ui->btn_open->setText("Open"); + ui->txt_fileName->setEnabled(true); + } + emit m->commonStatusChange(); + emit sendROScommand("OPEN", param); +} + +void RoboGlue_GUI::on_btn_realtime_clicked() { + static bool realtime = false; + QVariantMap param; + QVariantMap lock; + modlog->debug("robotTrajectory Realtime"); + if (!realtime){ + realtime = true; + m->commonStatus.isRealtime = true; + param["action"] = "start"; + param["lock"] = getLockAxes(); + if (ui->rad_pos->isChecked()) param["mode"] = "pos"; + else if (ui->rad_vel->isChecked()) param["mode"] = "vel"; + ui->btn_play->setEnabled(false); + ui->btn_record->setEnabled(false); + disableLockAxes(); + } else { + m->commonStatus.isRealtime = false; + realtime = false; + param["action"]="stop"; + ui->btn_play->setEnabled(true); + ui->btn_record->setEnabled(true); + enableLockAxes(); + } + emit m->commonStatusChange(); + emit sendROScommand("REALTIME", param); +} +//////////////////////////////////////////////// +////////END INTERNAL PRIVATE SLOTS////////////// +//////////////////////////////////////////////// + +//////////////////////////////////////////////// +////////////EXTERNAL PUBLIC SLOTS/////////////// +//////////////////////////////////////////////// +void RoboGlue_GUI::on_newRobotData() { + m->mutex.lock(); + robotModeData_t mode = m->robotData.robotMode; + cartesianInfo_t cart = m->robotData.cartesianInfo; + jointData_t joint = m->robotData.jointData; + m->mutex.unlock(); + + //////////// mode info ////////// + try { + ui->lbl_connected->setText(QString::number(mode.isRealRobotConnected)); + ui->lbl_robotMode->setText(QString(nameDefinitions["robotModes"][std::to_string(mode.robotMode).c_str()].get().c_str())); + ui->lbl_powerOn->setText(QString::number(mode.isRobotPowerOn)); + ui->lbl_emeStop->setText(QString::number(mode.isEmergencyStopped)); + ui->lbl_proStop->setText(QString::number(mode.isProtectiveStopped)); + ui->lbl_programRunning->setText(QString::number(mode.isProgramRunning)); + ui->lbl_controlMode->setText(QString(nameDefinitions["controlModes"][std::to_string(mode.controlMode).c_str()].get().c_str())); + } catch (json::exception e){ + modlog->error("Unable to find name: \n{}",e.what()); + } + //////////// cartesian info ////////// + ui->lbl_cartX->setText("X: "+QString::number(cart.cartPosition.X*1000)+QString(" mm")); + ui->lbl_cartY->setText("Y: "+QString::number(cart.cartPosition.Y*1000)+QString(" mm")); + ui->lbl_cartZ->setText("Z: "+QString::number(cart.cartPosition.Z*1000)+QString(" mm")); + ui->lbl_cartRX->setText("Rx: "+QString::number(cart.cartPosition.RX,'g',3)+QString(" rad")); + ui->lbl_cartRY->setText("Ry: "+QString::number(cart.cartPosition.RY,'g',3)+QString(" rad")); + ui->lbl_cartRZ->setText("Rz: "+QString::number(cart.cartPosition.RZ,'g',3)+QString(" rad")); + //////////// joint info ////////// + ui->lbl_J1->setText("Base: " + QString::number(joint.jointParam[0].actual)+" rad"); + ui->lbl_J2->setText("Shoulder: " + QString::number(joint.jointParam[1].actual)+" rad"); + ui->lbl_J3->setText("Elbow: " + QString::number(joint.jointParam[2].actual)+" rad"); + ui->lbl_J4->setText("Wrist1: " + QString::number(joint.jointParam[3].actual)+" rad"); + ui->lbl_J5->setText("Wrist2: " + QString::number(joint.jointParam[4].actual)+" rad"); + ui->lbl_J6->setText("Wrist3: " + QString::number(joint.jointParam[5].actual)+" rad"); +} +//////////////////////////////////////////////// +//////// END EXTERNAL PUBLIC SLOTS ///////////// +//////////////////////////////////////////////// + + diff --git a/gui/roboglue_gui.h b/gui/roboglue_gui.h new file mode 100644 index 0000000..ed94485 --- /dev/null +++ b/gui/roboglue_gui.h @@ -0,0 +1,108 @@ +#ifndef ROBOGLUE_GUI_H +#define ROBOGLUE_GUI_H + +/* + * roboglue_gui.cpp + * + * Created on: Jan 21, 2019 + * Author: emanuele + * + * Interfaccia utente di roboglue, conterrà la visualizzazione grafica del robot e tutte le + * interfaccce per il richiamo e la registrazione dei programmi di incollaggio + * tutta la logica sarà deferita ad un modulo main. + * + * 20190121 - Prima Scittura + * 20190122 - Disegnata la prima interfaccia grafica a scopo di debug, stampa solo le informazioni + * relative alla posizione cartesiana del robot. + * Impostata la struttura comune a tutti i moduli. + * 20190123 - Aggiunti elementi all'interfaccia grafica, connessione, disconnessione, indirizzo + * e porta del robot. + * 20190124 - Aggiunta logica per mandare comandi URscript senza controllo di ortografia e validità, + * aggiunta la lettura di un file json che include i nomi degli stati di robot + * e controller per una migliore leggibilità dell'interfaccia. + * Piccole modifiche alla grafica. + * 20190125 - Aggiunta una frame i cui fare il tracciamento del mouse per mandare posizioni + * in tempo reale. La Z dovrebbbe essere compito della rotella del mouse che però + * non genera eventi, trovare una soluzione (magari con la tastiera) per sostituirla. + * L'invio dei comandi al robot dalla gui è TEMPORANEO, sarà gestito dal main + * per fare il calcolo della cinematica inversa. + * 20190128 - Sostituita la lettura della rotella con quella dei tasti freccia, + * rimane da risolvere il fatto che quando non si usa il mouse le coordinate impazziscono + * (ma è solo per test quindi va bene così forse) + * 20190131 - Piccole modifiche alla grafica, aggiunte visualizzazioni posizione giunti + * 20190211 - Scrittura delle funzioni che mandano i comandi parametrizzati al modulo roboglue_com + * per l'invio di istruzioni a ros tramite mqtt. L'invio diretto al modulo com è solo + * temporaneo, le comunicazioni andranno gestite tutte dal main. + * 20190308 - Riscrittura delle funzioni associate ai bottoni di Record e Play per includere i metadati del + * file da scrivere o leggere + * 20190311 - Inversione del segno coordinata y prima dell'invio al modulo com. + * 20190322 - Modifica dei nomi segnali tra questo modulo e com. + * 20190402 - Aggiunte due spinbox per il settaggio di delta tempo e delta spazio da salvare nel + * file dei metadati per la pianificazione dei persorsi salvati +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { + class RoboGlue_GUI; + } + +using nlohmann::json; + +class RoboGlue_GUI : public QMainWindow { + Q_OBJECT + +public: + explicit RoboGlue_GUI(RoboGlue_SHARED *mem = nullptr); + ~RoboGlue_GUI(); + +private: + shared_ptr modlog; + RoboGlue_SHARED *m; + Ui::RoboGlue_GUI *ui; + bool eventFilter(QObject* obj, QEvent* event); + json nameDefinitions; + QVariantMap getLockAxes(void); + void enableLockAxes(void); + void disableLockAxes(void); + +public slots: + void on_commonStatusChange(void); + void on_newRobotData(); + +private slots: + void on_btn_sendCommand_clicked(); + void on_btn_robotConnect_clicked(); + void on_btn_robotDisconnect_clicked(); + void on_chk_autoConnect_clicked(bool checked); + void on_pad_hoverEvent(QEvent *e); + void on_btn_record_clicked(); + void on_btn_play_clicked(); + void on_btn_realtime_clicked(); + + void on_btn_open_clicked(); + +signals: + void robotConnect(QString, uint port, uchar retry); + void robotDisconnect(); + void robotSendURscript(QString); + + void sendROScoordinates(QList); + void sendROScommand(QString command, QVariantMap args); + void sendROSstate(QMap digital, QMap analog); + void pad_hoverEvent(QEvent *e); +}; + +#endif // ROBOGLUE_GUI_H diff --git a/gui/roboglue_gui.ui b/gui/roboglue_gui.ui new file mode 100644 index 0000000..715b84d --- /dev/null +++ b/gui/roboglue_gui.ui @@ -0,0 +1,1323 @@ + + + RoboGlue_GUI + + + + 0 + 0 + 945 + 750 + + + + false + + + MainWindow + + + + + + + 0 + + + + Robot + + + + + + Qt::Vertical + + + + + + + QLayout::SetMinimumSize + + + + + 0 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 150 + 16777215 + + + + Qt::LeftToRight + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 100 + 16777215 + + + + Connect + + + + + + + + 100 + 16777215 + + + + Disconnect + + + + + + + Auto + + + + + + + + 110 + 0 + + + + + 110 + 16777215 + + + + Robot IP + + + + + + + + 110 + 0 + + + + + 110 + 16777215 + + + + Robot Port + + + + + + + + + Qt::Horizontal + + + + + + + Robot Status + + + Qt::AlignCenter + + + + + + + QLayout::SetMaximumSize + + + + + + 150 + 100 + + + + - + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 120 + 16777215 + + + + Protective Stop + + + + + + + + 150 + 100 + + + + - + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 120 + 16777215 + + + + Control Mode + + + + + + + + 120 + 16777215 + + + + Robot Mode + + + + + + + + 120 + 16777215 + + + + Connected + + + + + + + + 120 + 16777215 + + + + Program Running + + + + + + + + 120 + 16777215 + + + + Power On + + + + + + + + 150 + 100 + + + + - + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 120 + 16777215 + + + + Emergency Stop + + + + + + + + 150 + 100 + + + + - + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 150 + 100 + + + + - + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 150 + 100 + + + + - + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + 150 + 100 + + + + - + + + Qt::AlignCenter + + + Qt::NoTextInteraction + + + + + + + + + Qt::Horizontal + + + + + + + + 16777215 + 16777215 + + + + Robot Position (Cartesian) + + + Qt::AlignCenter + + + + + + + QLayout::SetMaximumSize + + + + + + 150 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 150 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 150 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 150 + 16777215 + + + + 0 + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 150 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 150 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + + Qt::Horizontal + + + + + + + + 16777215 + 16777215 + + + + Robot Position (Joint) + + + Qt::AlignCenter + + + + + + + QLayout::SetMaximumSize + + + + + + 200 + 16777215 + + + + 0 + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 200 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 200 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 200 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 200 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + 200 + 16777215 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::TextSelectableByMouse + + + + + + + + + Qt::Horizontal + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + + + + i: + + + 15 + + + + + + + + 96 + 0 + + + + - + + + Qt::AlignCenter + + + + + + + GET IO + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + o: + + + 15 + + + + + + + + 45 + 0 + + + + HI + + + grp_hilo + + + + + + + + 45 + 0 + + + + LO + + + true + + + grp_hilo + + + + + + + SET IO + + + + + + + + + + + Qt::Horizontal + + + + + + + QLayout::SetMaximumSize + + + + + + 300 + 16777215 + + + + + + + + + 120 + 16777215 + + + + Send UR_script + + + + + + + + + <html><head/><body><p align="center"><span style=" font-weight:600; text-decoration: underline; color:#ef2929;">ATTENZIONE<br/> NESSUN CHECK È EFFETTUATO SUL COMANDO!</span></p></body></html> + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 400 + 400 + + + + + 400 + 400 + + + + CrossCursor + + + true + + + QFrame::StyledPanel + + + QFrame::Raised + + + 4 + + + + + + + Qt::Horizontal + + + + + + + + + CursorPosition + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + + Qt::Horizontal + + + + + + + + + Record + + + false + + + + + + + Play + + + + + + + Open + + + + + + + Filename + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + RealTime + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + MODE: + + + + + + + + + POS + + + true + + + grp_mod + + + + + + + VEL + + + grp_mod + + + + + + + + + Qt::Vertical + + + + + + + LOCK: + + + + + + + + + RY + + + gpr_lock + + + + + + + RX + + + gpr_lock + + + + + + + RZ + + + gpr_lock + + + + + + + X + + + gpr_lock + + + + + + + Y + + + gpr_lock + + + + + + + Z + + + gpr_lock + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + dS + + + Qt::AlignCenter + + + + + + + dT + + + Qt::AlignCenter + + + + + + + 100.000000000000000 + + + 0.010000000000000 + + + QAbstractSpinBox::DefaultStepType + + + 0.500000000000000 + + + + + + + 240.000000000000000 + + + 0.010000000000000 + + + 0.500000000000000 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Main + + + + + Tracker + + + + + + + + + + 0 + 0 + 945 + 22 + + + + + File + + + + + + + + + Settings + + + + + + + + + + + false + + + + diff --git a/init/roboglue_init.cpp b/init/roboglue_init.cpp new file mode 100644 index 0000000..9f65858 --- /dev/null +++ b/init/roboglue_init.cpp @@ -0,0 +1,196 @@ +#include "roboglue_init.h" +#include "ui_roboglue_init.h" + +RoboGlue_INIT::RoboGlue_INIT(QWidget *parent, const char*) : QMainWindow(parent) { + + //////// SETUP LOGGER ////////// + inilog = spdlog::stdout_logger_mt("RoboGlue_inilog"); + inilog->info("INIT Started"); + + //////// SETUP USER INTERFACE /////// + ui = new Ui::RoboGlue_INIT; + ui->setupUi(this); + + //////// INITIALIZE MEMORY MODULE //////////// + QCoreApplication::setOrganizationName("ETsoft"); + QCoreApplication::setApplicationName("RoboGlue"); + settings = new QSettings("../RoboGlue/roboglue.conf", QSettings::IniFormat); + sharedmem = new RoboGlue_SHARED(settings); + + //////// RESTORE SETTINGS FOR ALL MODULES //////////// + sharedmem->restoreInitSettings(); + sharedmem->restoreComSettings(); + sharedmem->restoreGuiSettings(); + sharedmem->restoreMainSettings(); + sharedmem->restoreTrackSettings(); + + //////// RESTORE UI STATE //////////// + ui->chk_comAuto->setChecked(sharedmem->initSettings.autoCom); + ui->chk_guiAuto->setChecked(sharedmem->initSettings.autoGui); + ui->chk_mainAuto->setChecked(sharedmem->initSettings.autoMain); + ui->chk_trackAuto->setChecked(sharedmem->initSettings.autoTrack); + + //////// AUTO START MODULES ////////// + if (sharedmem->initSettings.autoGui) this->on_btn_guiStart_clicked(); + if (sharedmem->initSettings.autoMain) this->on_btn_mainStart_clicked(); + if (sharedmem->initSettings.autoCom) this->on_btn_comStart_clicked(); + if (sharedmem->initSettings.autoTrack) this->on_btn_trackStart_clicked(); + + //////// CONNECT SIGNALS & SLOTS /////// + connect(this, SIGNAL(destroyed()), this, SLOT(deleteLater()), Qt::ConnectionType::QueuedConnection); +} + +RoboGlue_INIT::~RoboGlue_INIT() { + // close order must be opposite of opening one + on_btn_trackStop_clicked(); + on_btn_comStop_clicked(); + on_btn_mainStop_clicked(); + on_btn_guistop_clicked(); + + sharedmem->saveInitSettings(); + sharedmem->saveMainSettings(); + sharedmem->saveTrackSettings(); + sharedmem->saveComSettings(); + sharedmem->saveGuiSettings(); + + delete robot; + delete gui; + delete main; + delete track; + delete settings; + delete sharedmem; + delete ui; +} + + +/////////////////////////////////////////////////// +/////////////// HEARTBEAT SLOTS /////////////////// +/////////////////////////////////////////////////// +void RoboGlue_INIT::on_robotHeartBeat(){ + +} +void RoboGlue_INIT::on_guiHeartBeat(){ + +} + +void RoboGlue_INIT::on_mainHeartBeat(){ + +} + +void RoboGlue_INIT::on_trackHeartBeat(){ + +} +/////////////////////////////////////////////////// +/////////////// BUTTON SLOTS ////////////////////// +/////////////////////////////////////////////////// +void RoboGlue_INIT::on_btn_comStart_clicked(){ + if (robot == nullptr) { + robot = new RoboGlue_COM(sharedmem); + robot->start(); + // connect signals + if (gui != nullptr){ + // robot to gui + connect(robot, SIGNAL(com_newData(void)), + gui, SLOT(on_newRobotData(void)), Qt::ConnectionType::QueuedConnection); + //gui to robot + connect(gui, SIGNAL(sendROScommand(QString, QVariantMap)), + robot, SLOT(on_sendROScommand(QString,QVariantMap)), + Qt::ConnectionType::QueuedConnection); + connect(gui, SIGNAL(sendROScoordinates(QList)), + robot, SLOT(on_sendROScoordinates(QList)), + Qt::ConnectionType::QueuedConnection); + connect(gui, SIGNAL(sendROSstate(QMap, QMap)), + robot, SLOT(on_sendROSstate(QMap, QMap)), + Qt::ConnectionType::QueuedConnection); + connect(gui, SIGNAL(robotSendURscript(QString)), + robot, SLOT(on_sendURscript(QString)), + Qt::ConnectionType::QueuedConnection); + connect(gui, SIGNAL(robotConnect(QString, uint, uchar)), + robot, SLOT(on_connect(QString, uint, uchar)), + Qt::ConnectionType::QueuedConnection); + connect(gui, SIGNAL(robotDisconnect()), + robot, SLOT(on_disconnect()), + Qt::ConnectionType::QueuedConnection); + } + // robot to init + connect(robot, SIGNAL(com_heartBeat(void)), + this, SLOT(on_robotHeartBeat(void)), Qt::ConnectionType::QueuedConnection); + // shared memory to robot + connect(sharedmem, SIGNAL(commonStatusChange(void)), + robot, SLOT(on_commonStatusChange(void))); + } +} + +void RoboGlue_INIT::on_btn_comStop_clicked() { + if (robot == nullptr) return; + robot->on_quit(); + while (!robot->isFinished()){ + QThread::msleep(100); + } + robot->deleteLater(); + robot=nullptr; +} + +void RoboGlue_INIT::on_btn_guiStart_clicked() { + if (gui == nullptr) { + gui = new RoboGlue_GUI(sharedmem); + gui->show(); + // shared memory to robot + connect(sharedmem, SIGNAL(commonStatusChange(void)), + gui, SLOT(on_commonStatusChange(void))); + if (robot != nullptr){ + } + } +} + +void RoboGlue_INIT::on_btn_guistop_clicked() { + if (gui == nullptr) return; + gui->deleteLater(); + gui = nullptr; +} + +void RoboGlue_INIT::on_btn_mainStart_clicked() { + if (main == nullptr){ + main = new RoboGlue_MAIN(sharedmem); + main->start(); + // connect signals + // connect() + } +} + +void RoboGlue_INIT::on_btn_mainStop_clicked(){ + if (main == nullptr) return; + main->deleteLater(); + main = nullptr; +} + +void RoboGlue_INIT::on_btn_trackStart_clicked() { + if (track == nullptr){ + track = new RoboGlue_TRACK(sharedmem); + track->start(); + // connect signals + // connect() + } +} + +void RoboGlue_INIT::on_btn_trackStop_clicked() { + if (track == nullptr) return; + track->deleteLater(); + track = nullptr; +} + +void RoboGlue_INIT::on_chk_comAuto_clicked(bool checked) { + sharedmem->initSettings.autoCom=checked; +} + +void RoboGlue_INIT::on_chk_guiAuto_clicked(bool checked) { + sharedmem->initSettings.autoGui=checked; +} + +void RoboGlue_INIT::on_chk_trackAuto_clicked(bool checked) { + sharedmem->initSettings.autoTrack=checked; +} + +void RoboGlue_INIT::on_chk_mainAuto_clicked(bool checked){ + sharedmem->initSettings.autoMain=checked; +} diff --git a/init/roboglue_init.h b/init/roboglue_init.h new file mode 100644 index 0000000..2788357 --- /dev/null +++ b/init/roboglue_init.h @@ -0,0 +1,61 @@ +#ifndef ROBOGLUE_INIT_H +#define ROBOGLUE_INIT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include
+ +namespace Ui { +class RoboGlue_INIT; +} + +class RoboGlue_INIT : public QMainWindow +{ + Q_OBJECT + +public: + explicit RoboGlue_INIT(QWidget *parent = nullptr, const char* settingsFile = nullptr); + virtual ~RoboGlue_INIT(); + +private: + shared_ptr inilog; + + Ui::RoboGlue_INIT *ui; + + QSettings *settings; + RoboGlue_SHARED *sharedmem; + RoboGlue_COM *robot = nullptr; + RoboGlue_GUI *gui = nullptr; + RoboGlue_MAIN *main = nullptr; + RoboGlue_TRACK *track = nullptr; + +private slots: + // modules heartbeat slots + void on_robotHeartBeat(); + void on_guiHeartBeat(); + void on_mainHeartBeat(); + void on_trackHeartBeat(); + // button slots + void on_btn_comStart_clicked(); + void on_btn_guiStart_clicked(); + void on_btn_comStop_clicked(); + void on_btn_guistop_clicked(); + void on_btn_mainStart_clicked(); + void on_btn_trackStart_clicked(); + void on_btn_mainStop_clicked(); + void on_btn_trackStop_clicked(); + // checkbox slots + void on_chk_comAuto_clicked(bool checked); + void on_chk_guiAuto_clicked(bool checked); + void on_chk_trackAuto_clicked(bool checked); + void on_chk_mainAuto_clicked(bool checked); +}; + +#endif // ROBOGLUE_INIT_H diff --git a/init/roboglue_init.ui b/init/roboglue_init.ui new file mode 100644 index 0000000..339eddf --- /dev/null +++ b/init/roboglue_init.ui @@ -0,0 +1,279 @@ + + + RoboGlue_INIT + + + + 0 + 0 + 499 + 248 + + + + RoboGlue_INIT + + + + + + + <html><head/><body><p><span style=" font-size:20pt;">RoboGlue_INIT</span></p></body></html> + + + Qt::RichText + + + Qt::AlignCenter + + + + + + + + + + 50 + 16777215 + + + + + + + + + + + STOP + + + + + + + Module + + + Qt::AlignCenter + + + + + + + false + + + Auto + + + + + + + START + + + + + + + false + + + RoboGlue_MAIN + + + + + + + Auto + + + + + + + RoboGlue_GUI + + + + + + + false + + + + 50 + 16777215 + + + + + + + + + + + false + + + START + + + + + + + STOP + + + + + + + RoboGlue_COM + + + + + + + Status + + + Qt::AlignCenter + + + + + + + START + + + + + + + false + + + STOP + + + + + + + AutoStart + + + Qt::AlignCenter + + + + + + + Auto + + + + + + + + 50 + 16777215 + + + + + + + + + + + false + + + RoboGlue_TRACK + + + + + + + false + + + START + + + + + + + false + + + STOP + + + + + + + false + + + Auto + + + + + + + false + + + + 50 + 16777215 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/libJson/json.hpp b/libJson/json.hpp new file mode 100644 index 0000000..c9af0be --- /dev/null +++ b/libJson/json.hpp @@ -0,0 +1,20406 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.5.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2018 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 5 +#define NLOHMANN_JSON_VERSION_PATCH 0 + +#include // all_of, find, for_each +#include // assert +#include // and, not, or +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // random_access_iterator_tag +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap + +// #include +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; +} // namespace nlohmann + +#endif + +// #include + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// manual branch prediction +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) + #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + #define JSON_LIKELY(x) x + #define JSON_UNLIKELY(x) x +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// #include + + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // not +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} +} + +// #include + +// #include + + +#include + +// #include + + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template