commit 44ac6e88027247f0c3dde5b099cd71dbf32eaa96 Author: Emanuele Date: Mon Oct 21 11:37:31 2019 +0200 Commit Iniziale 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 0000000..66bb22a Binary files /dev/null and b/PiantaRobot_Alto.png differ 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