From 69d561c8425f0f45921a9dfc66e6a5514ed081f3 Mon Sep 17 00:00:00 2001 From: Fabien-B Date: Tue, 27 Apr 2021 09:26:25 +0200 Subject: [PATCH] Adapt old lib to IvyQt. --- PprzlinkQt/CMakeLists.txt | 54 -- PprzlinkQt/message.cpp | 145 ------ PprzlinkQt/message.h | 47 -- PprzlinkQt/message_definition.cpp | 97 ---- PprzlinkQt/message_definition.h | 62 --- PprzlinkQt/pprzlinkqt.cpp | 101 ---- PprzlinkQt/pprzlinkqt.h | 35 -- pprzlinkQt/CMakeLists.txt | 89 ++++ pprzlinkQt/include/pprzlinkQt/Device.h | 58 +++ pprzlinkQt/include/pprzlinkQt/FieldValue.h | 357 +++++++++++++ pprzlinkQt/include/pprzlinkQt/IvyQtLink.h | 55 ++ pprzlinkQt/include/pprzlinkQt/Message.h | 204 ++++++++ .../include/pprzlinkQt/MessageDefinition.h | 69 +++ .../include/pprzlinkQt/MessageDictionary.h | 56 +++ pprzlinkQt/include/pprzlinkQt/MessageField.h | 52 ++ .../include/pprzlinkQt/MessageFieldTypes.h | 64 +++ pprzlinkQt/include/pprzlinkQt/old/IvyLink.cpp | 208 ++++++++ pprzlinkQt/include/pprzlinkQt/old/IvyLink.h | 182 +++++++ .../include/pprzlinkQt/pprzlink_exception.h | 55 ++ pprzlinkQt/src/FieldValue.cpp | 476 ++++++++++++++++++ pprzlinkQt/src/IvyQtLink.cpp | 331 ++++++++++++ pprzlinkQt/src/Message.cpp | 401 +++++++++++++++ pprzlinkQt/src/MessageDefinition.cpp | 131 +++++ pprzlinkQt/src/MessageDictionary.cpp | 189 +++++++ pprzlinkQt/src/MessageField.cpp | 66 +++ pprzlinkQt/src/MessageFieldTypes.cpp | 125 +++++ 26 files changed, 3168 insertions(+), 541 deletions(-) delete mode 100644 PprzlinkQt/CMakeLists.txt delete mode 100644 PprzlinkQt/message.cpp delete mode 100644 PprzlinkQt/message.h delete mode 100644 PprzlinkQt/message_definition.cpp delete mode 100644 PprzlinkQt/message_definition.h delete mode 100644 PprzlinkQt/pprzlinkqt.cpp delete mode 100644 PprzlinkQt/pprzlinkqt.h create mode 100644 pprzlinkQt/CMakeLists.txt create mode 100644 pprzlinkQt/include/pprzlinkQt/Device.h create mode 100644 pprzlinkQt/include/pprzlinkQt/FieldValue.h create mode 100644 pprzlinkQt/include/pprzlinkQt/IvyQtLink.h create mode 100644 pprzlinkQt/include/pprzlinkQt/Message.h create mode 100644 pprzlinkQt/include/pprzlinkQt/MessageDefinition.h create mode 100644 pprzlinkQt/include/pprzlinkQt/MessageDictionary.h create mode 100644 pprzlinkQt/include/pprzlinkQt/MessageField.h create mode 100644 pprzlinkQt/include/pprzlinkQt/MessageFieldTypes.h create mode 100644 pprzlinkQt/include/pprzlinkQt/old/IvyLink.cpp create mode 100644 pprzlinkQt/include/pprzlinkQt/old/IvyLink.h create mode 100644 pprzlinkQt/include/pprzlinkQt/pprzlink_exception.h create mode 100644 pprzlinkQt/src/FieldValue.cpp create mode 100644 pprzlinkQt/src/IvyQtLink.cpp create mode 100644 pprzlinkQt/src/Message.cpp create mode 100644 pprzlinkQt/src/MessageDefinition.cpp create mode 100644 pprzlinkQt/src/MessageDictionary.cpp create mode 100644 pprzlinkQt/src/MessageField.cpp create mode 100644 pprzlinkQt/src/MessageFieldTypes.cpp diff --git a/PprzlinkQt/CMakeLists.txt b/PprzlinkQt/CMakeLists.txt deleted file mode 100644 index 53f9766..0000000 --- a/PprzlinkQt/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -project(PprzlinkQt LANGUAGES CXX) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Xml REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS - Core - Xml - Network - REQUIRED) - -set(IvyQt_DIR "../IvyQt/IvyQt/") -add_subdirectory(${IvyQt_DIR} ivyqt) - -add_library(PprzlinkQt STATIC - pprzlinkqt.cpp - message_definition.cpp - message.cpp -) - -target_include_directories(PprzlinkQt PRIVATE ${IvyQt_DIR}) - -#set_target_properties(PprzlinkQt PROPERTIES -# VERSION 0.0.1 -#) - -target_link_libraries(PprzlinkQt PRIVATE - Qt${QT_VERSION_MAJOR}::Core - Qt${QT_VERSION_MAJOR}::Xml - Qt${QT_VERSION_MAJOR}::Network -) - - -target_compile_definitions(PprzlinkQt PRIVATE PPRZLINKQT_LIBRARY) - - -install(TARGETS PprzlinkQt DESTINATION lib) -install(FILES - pprzlinkqt.h - message_definition.h - message.h - DESTINATION include) - - - - - diff --git a/PprzlinkQt/message.cpp b/PprzlinkQt/message.cpp deleted file mode 100644 index e16c9eb..0000000 --- a/PprzlinkQt/message.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "message.h" - -Message::Message(MessageDefinition definition): definition(definition) -{ - -} - -Message::Message(MessageDefinition definition, QStringList values) : definition(definition) -{ - assert(definition.getFields().size() == values.size()); - - auto defs = definition.getFields(); - for(int i=0; itype == Type::U8) { - uint8_t val; - getField(field_name, val); - return QString::number(val); - } else if(def->type == Type::I8) { - int8_t val; - getField(field_name, val); - return QString::number(val); - } else if(def->type == Type::U16) { - uint16_t val; - getField(field_name, val); - return QString::number(val); - } else if(def->type == Type::I16) { - int16_t val; - getField(field_name, val); - return QString::number(val); - } else if(def->type == Type::U32) { - uint32_t val; - getField(field_name, val); - return QString::number(val); - } else if(def->type == Type::I32) { - int32_t val; - getField(field_name, val); - return QString::number(val); - } else if(def->type == Type::FLOAT) { - float val; - getField(field_name, val); - return QString::number(val); - } else if(def->type == Type::STRING) { - QString val; - getField(field_name, val); - return val; - } else if(def->type == Type::ARRAY) { - throw std::runtime_error("arrays not done yet!"); - } - - throw std::runtime_error("unknown type!"); -} - -template -void Message::addField(QString name, T value) { - - auto field_defs = definition.getFields(); - - auto def = std::find_if(field_defs.begin(), field_defs.end(), - [=](FieldDef fd){return fd.name == name;}); - if(def == field_defs.end()) { - throw std::runtime_error("No such field: \"" + name.toStdString() + "\"!"); - } - - bool type_ok = false; - if(def->type == Type::U8) { - type_ok = std::is_same_v; - } else if(def->type == Type::U16) { - type_ok = std::is_same_v; - } else if(def->type == Type::U32) { - type_ok = std::is_same_v; - } else if(def->type == Type::I8) { - type_ok = std::is_same_v; - } else if(def->type == Type::I16) { - type_ok = std::is_same_v; - } else if(def->type == Type::I32) { - type_ok = std::is_same_v; - } else if(def->type == Type::FLOAT) { - type_ok = std::is_same_v; - } else if(def->type == Type::STRING) { - type_ok = std::is_same_v; - } else if(def->type == Type::CHAR) { - type_ok = std::is_same_v; - } - - if(!type_ok) { - throw std::runtime_error("Bad type!"); - } - - fields[name] = std::make_any(value); -} - -void Message::getField(QString field_name, QString& arg){ - if(!fields.contains(field_name)) { - throw std::runtime_error("no such field!"); - } - arg = std::any_cast(fields[field_name]); -} - -bool Message::isComplete() { - // fields are checked at insertion, - // so just check if the number of fields match - return definition.getFields().size() == fields.size(); -} - diff --git a/PprzlinkQt/message.h b/PprzlinkQt/message.h deleted file mode 100644 index c6a0ebc..0000000 --- a/PprzlinkQt/message.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef MESSAGE_H -#define MESSAGE_H - -#include -#include "message_definition.h" - - -class Message -{ -public: - Message(MessageDefinition definition); - Message(MessageDefinition definition, QStringList values); - - bool isComplete(); - QString getSender() { return sender;} - void setSender(QString s) { - sender = s; - } - MessageDefinition getDefinition() { return definition;} - - template< - typename T, - typename E = typename std::enable_if::value>::type - > - void getField(QString field_name, T& arg){ - if(!fields.contains(field_name)) { - throw std::runtime_error("No such field: \"" + field_name.toStdString() + "\"!"); - } - arg = std::any_cast(fields[field_name]); - } - - QString getField(QString field_name); - - void getField(QString field_name, QString& arg); - - template - void addField(QString name, T value); - - MessageDefinition messageDefinition() const {return definition;} - -private: - MessageDefinition definition; - QString sender; - QMap fields; -}; - -#endif // MESSAGE_H diff --git a/PprzlinkQt/message_definition.cpp b/PprzlinkQt/message_definition.cpp deleted file mode 100644 index cf0acda..0000000 --- a/PprzlinkQt/message_definition.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "message_definition.h" - - -MessageDefinition::MessageDefinition(QDomElement msg_elt, QString class_name, uint8_t class_id): - class_name(class_name), class_id(class_id) -{ - name = msg_elt.attribute("name"); - id = msg_elt.attribute("id").toUInt(); - - for(auto elt = msg_elt.firstChildElement(); - !elt.isNull(); - elt = elt.nextSiblingElement()) { - - if(elt.tagName() == "description") { - description = elt.text(); - } else if(elt.tagName() == "field") { - - struct FieldDef field = { - elt.attribute("name"), - typeFromString(elt.attribute("type")), - elt.attribute("format"), - elt.attribute("unit"), - elt.attribute("values"), - elt.attribute("alt_unit"), - elt.attribute("alt_unit_coef"), - elt.text(), - }; - - fields.append(field); - - } - } -} - -QString MessageDefinition::toRegex() const { - QString regex = QString("^([^ ]+) %1").arg(name); // - - for(auto &f: qAsConst(fields)) { - regex += QString(" %1").arg(regexOfField(f)); - } - regex += "$"; - return regex; -} - - -Type MessageDefinition::typeFromString(QString type) { - if(type == "uint8") { - return Type::U8; - } else if(type == "int8") { - return Type::I8; - } else if(type == "uint16") { - return Type::U16; - } else if(type == "int16") { - return Type::I16; - } else if(type == "uint32") { - return Type::U32; - } else if(type == "int32") { - return Type::I32; - } else if(type == "float") { - return Type::FLOAT; - } else if(type == "string") { - return Type::STRING; - } else if(type == "char") { - return Type::CHAR; - } else if(type.contains("[") && type.contains("]")) { - return Type::ARRAY; - } else { - return Type::UNKNOWN; - qDebug() << "Type " << type << "unknown!!!"; - } -} - -QString MessageDefinition::regexOfField(FieldDef field) { - if(field.type == U8 || - field.type == U16 || - field.type == U32) { - return "(\\d+)"; - } else if(field.type == I8 || - field.type == I16 || - field.type == I32) { - return "(-?\\d+)"; - } else if(field.type == FLOAT) { - return "(-?\\d+(?:\\.)?(?:\\d)*)"; - } else if(field.type == STRING) { - return "((?:\"[^\"]+\"|[^ ]+))"; - } else if(field.type == CHAR) { - return "TODO"; - } else if(field.type == ARRAY) { - return "TODO"; - } else if(field.type == UNKNOWN) { - throw std::runtime_error("unknown type"); - } else { - throw std::runtime_error("very unknown type"); - } - - return "plop"; -} diff --git a/PprzlinkQt/message_definition.h b/PprzlinkQt/message_definition.h deleted file mode 100644 index 4f6b3f8..0000000 --- a/PprzlinkQt/message_definition.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef MESSAGEDEFINITION_H -#define MESSAGEDEFINITION_H - -#include -#include -#include - -enum Type { - CHAR, - U8, - U16, - U32, - I8, - I16, - I32, - FLOAT, - ARRAY, //treat it has string for now - STRING, - UNKNOWN, -}; - -struct FieldDef { - QString name; - Type type; - QString format; - QString unit; - QString values; - QString alt_unit; - QString alt_unit_coef; - QString description; -}; - -class MessageDefinition -{ -public: - MessageDefinition(QDomElement msg_elt, QString class_name, uint8_t class_id); - - QString getName() const {return name;} - uint8_t getId() const {return id;} - QString getClassName() const {return class_name;} - uint8_t getClassId() const {return class_id;} - QString getDescription() const {return description;} - QList getFields() {return fields;} - QString toRegex() const; - - -private: - - static Type typeFromString(QString type); - static QString regexOfField(FieldDef field); - - QString class_name; - uint8_t class_id; - QString name; - uint8_t id; - QString description; - - QList fields; - -}; - -#endif // MESSAGEDEFINITION_H diff --git a/PprzlinkQt/pprzlinkqt.cpp b/PprzlinkQt/pprzlinkqt.cpp deleted file mode 100644 index 119efb7..0000000 --- a/PprzlinkQt/pprzlinkqt.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "pprzlinkqt.h" -#include "ivyqt.h" -#include - - -PprzlinkQt::PprzlinkQt(QString filename, QString ivy_name, QString msg_ready, QObject *parent) : QObject(parent) -{ - parse_definitions(filename); - ivyqt = new IvyQt(ivy_name, msg_ready, this); -} - - -void PprzlinkQt::parse_definitions(QString filename) { - QDomDocument xmlMessages; - QFile f(filename); - if(!f.open(QIODevice::ReadOnly)) { - throw std::runtime_error("Error while loading layout file"); - } - xmlMessages.setContent(&f); - f.close(); - - QDomElement root = xmlMessages.documentElement(); - - QString rootTag = root.tagName(); - - if(rootTag != "protocol") { - throw std::runtime_error("Root tag expected to be \"protocol\". Is this a messages file ?"); - } - - for(auto class_elt = root.firstChildElement(); - !class_elt.isNull(); - class_elt = class_elt.nextSiblingElement()) { - - QString class_name = class_elt.attribute("name"); - uint8_t class_id = class_elt.attribute("id").toUInt(); - //qDebug() << class_name << class_id; - - for(auto msg_elt = class_elt.firstChildElement(); - !msg_elt.isNull(); - msg_elt = msg_elt.nextSiblingElement()) { - - MessageDefinition def(msg_elt, class_name, class_id); - message_definitions.append(def); - } - - } -} - -void PprzlinkQt::start(QString domain, int port) { - ivyqt->start(domain, port); -} - -void PprzlinkQt::stop() { - ivyqt->stop(); -} - -int PprzlinkQt::bind(QString msg_name, std::function callback) { - return bind(msg_name, nullptr, callback); -} - -int PprzlinkQt::bind(QString msg_name, QObject* context, std::function callback) { - auto def = getDefinition(msg_name); - return ivyqt->bindMessage(def.toRegex(), context, [=](Peer* peer, QStringList params) { - (void)peer; - QString sender = params.takeFirst(); - Message msg(def, params); - callback(sender, msg); - }); -} - -MessageDefinition PprzlinkQt::getDefinition(QString msg_name) { - for(auto &def: qAsConst(message_definitions)) { - if(def.getName() == msg_name) { - return def; - } - } - throw std::runtime_error("no such message: " + msg_name.toStdString() + "!"); -} - -void PprzlinkQt::unBind(int bindId) { - ivyqt->unBindMessage(bindId); -} - -void PprzlinkQt::sendMessage(Message &msg) { - assert(msg.isComplete()); - auto message = stringOfMessage(msg); - qDebug() << message; - ivyqt->send(message); -} - -QString PprzlinkQt::stringOfMessage(Message &msg) { - QString str = msg.getSender() + " " + msg.getDefinition().getName(); - - for(auto fdef: msg.getDefinition().getFields()) { - str += " " + msg.getField(fdef.name); - } - - //TODO - - return str; -} diff --git a/PprzlinkQt/pprzlinkqt.h b/PprzlinkQt/pprzlinkqt.h deleted file mode 100644 index 39f2fdb..0000000 --- a/PprzlinkQt/pprzlinkqt.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef PPRZLINKQT_H -#define PPRZLINKQT_H - -#include -#include "message_definition.h" -#include "message.h" - -class IvyQt; - -class PprzlinkQt : public QObject -{ - Q_OBJECT -public: - explicit PprzlinkQt(QString filename, QString ivy_name, QString msg_ready, QObject *parent = nullptr); - void start(QString domain, int port); - void stop(); - int bind(QString msg_name, QObject* context, std::function); - int bind(QString msg_name, std::function); - void unBind(int bindId); - void sendMessage(Message &msg); - MessageDefinition getDefinition(QString msg_name); - -signals: - -private: - static QString stringOfMessage(Message &msg); - void parse_definitions(QString filename); - QList message_definitions; - //QMap bindings; - - IvyQt* ivyqt; - -}; - -#endif // PPRZLINKQT_H diff --git a/pprzlinkQt/CMakeLists.txt b/pprzlinkQt/CMakeLists.txt new file mode 100644 index 0000000..4195ca8 --- /dev/null +++ b/pprzlinkQt/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 3.14) + +project(pprzlinkQt LANGUAGES CXX) + +include(GNUInstallDirs) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Xml REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS + Core + Xml + Network + REQUIRED) + +set(IvyQt_DIR "../IvyQt/IvyQt/") +add_subdirectory(${IvyQt_DIR} ivyqt) + +set(SOURCES + src/FieldValue.cpp + src/Message.cpp + src/MessageDefinition.cpp + src/MessageDictionary.cpp + src/MessageField.cpp + src/MessageFieldTypes.cpp + src/IvyQtLink.cpp + include/pprzlinkQt/IvyQtLink.h +) + + +#add_library(PprzlinkQt_static STATIC ${SOURCES}) +add_library(pprzlinkQt SHARED ${SOURCES}) + +target_include_directories( + ${PROJECT_NAME} + PRIVATE + ${IvyQt_DIR} + PUBLIC $ + $) +#target_include_directories(PprzlinkQt_static PRIVATE ${IvyQt_DIR}) + +#set_target_properties(PprzlinkQt PROPERTIES +# VERSION 0.0.1 +#) + +set(LINK_LIBRARIES + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Xml + Qt${QT_VERSION_MAJOR}::Network + IvyQt +) + +target_link_libraries(pprzlinkQt PRIVATE ${LINK_LIBRARIES}) +#target_link_libraries(PprzlinkQt_static PRIVATE ${LINK_LIBRARIES}) + + +target_compile_definitions(pprzlinkQt PRIVATE PPRZLINKQT_LIBRARY) +#target_compile_definitions(PprzlinkQt_static PRIVATE PPRZLINKQT_LIBRARY) + + +install(TARGETS pprzlinkQt + EXPORT ${PROJECT_NAME}Config + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + + +install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/${PROJECT_NAME}/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) + +export(TARGETS + pprzlinkQt + FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" +) + +install(EXPORT ${PROJECT_NAME}Config DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake") + + + + + + diff --git a/pprzlinkQt/include/pprzlinkQt/Device.h b/pprzlinkQt/include/pprzlinkQt/Device.h new file mode 100644 index 0000000..9d72134 --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/Device.h @@ -0,0 +1,58 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file Device.h + * + * + */ + +#ifndef PPRZLINKCPP_DEVICE_H +#define PPRZLINKCPP_DEVICE_H + +#include +#include +#include + +/* +using check_free_space_t = std::function; +using put_byte_t = std::function; +using put_buffer_t = std::function; +using send_message_t = std::function; +using char_available_t = std::function; +using get_byte_t = std::function; +using set_baudrate_t = std::function; +*/ + +namespace pprzlink { + + using BytesBuffer = std::vector; + + /** + * + */ + class Device { + public: + virtual size_t availableBytes() = 0; + + virtual BytesBuffer readAll() = 0; + + virtual void writeBuffer(BytesBuffer const &data) = 0; + }; +} +#endif //PPRZLINKCPP_DEVICE_H diff --git a/pprzlinkQt/include/pprzlinkQt/FieldValue.h b/pprzlinkQt/include/pprzlinkQt/FieldValue.h new file mode 100644 index 0000000..873889f --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/FieldValue.h @@ -0,0 +1,357 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file FieldValue.h + * + * + */ + + +#ifndef PPRZLINKCPP_FIELDVALUE_H +#define PPRZLINKCPP_FIELDVALUE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace pprzlink { + /** + * TODO + */ + class FieldValue { + public: + /** + * Default constructor of FieldValue. + * The constructed FieldValue is not usable (used only for adding easily in containers) + */ + FieldValue() : field("","char") {} + + /** + * TODO + * @tparam T + * @param field + * @param array + * @param size + */ + template< + typename T, + typename = typename std::enable_if::value>::type + > + FieldValue(const MessageField &field, const T *array, size_t size) : field(field) + { + const auto &type = field.getType(); + if (type.isArray()) + { + if (type.getArraySize() && type.getArraySize() != size) + { + QString sstr = "Wrong size in building value for " + field.getName() + ", got " + QString::number(size) + " / expected " + + type.getArraySize(); + throw std::logic_error(sstr.toStdString()); + } + std::vector vec; + vec.resize(size); + for (size_t i = 0; i < size; ++i) + { + vec[i] = MakeStdAny(field, array[i]); + } + value = std::any(vec); + } + else + { + throw std::logic_error("Cannot build scalar field from an array"); + } + } + + /** Builds a FieldValue from a string. + * This will build either a string type or a char array type. + * No parsing is done for other types. + * + * @tparam T this will resolve to QString + * @param field The MessageField for which the value is built + * @param str The string to build the value from + */ + template< + typename T, + typename = typename std::enable_if::value>::type + > + FieldValue(const MessageField &field, const T &str) : field(field) + { + const auto &type = field.getType(); + const auto &name = field.getName(); + if (type.getBaseType() != BaseType::STRING && !(type.isArray() && type.getBaseType() == BaseType::CHAR)) + { + throw std::logic_error("Cannot build field " + name.toStdString() + " of type " + type.toString().toStdString() + " from string value."); + } + if (type.getBaseType() == BaseType::STRING) + { + value = std::any(QString(str)); + } + else + { + // If it is a char array, treat this as any other array + std::vector v; + if (type.getArraySize() && type.getArraySize() != str.size()) + { + QString sstr = "Wrong size in building value for " + field.getName() + ", got " + QString::number(str.size()) + " / expected " + + QString::number(type.getArraySize()); + throw std::logic_error(sstr.toStdString()); + } + v.reserve(str.size()); + for (auto val: QString(str)) + { + v.push_back(MakeStdAny(field, val.toLatin1())); + } + value = std::any(v); + } + } + + + /** + * TODO + * @tparam T + * @param field + * @param s + */ + template< + typename T, + typename = typename std::enable_if::value>::type + > + FieldValue(const MessageField &field, const T *s) : FieldValue(field, QString(s)) + { + } + + /** + * TODO + * @tparam Container + * @tparam T + * @param field + * @param c + */ + template< + typename Container, + typename T = typename Container::value_type, + typename = typename std::enable_if< + !std::is_arithmetic::value && !std::is_same::value>::type + > + FieldValue(const MessageField &field, const Container &c) : field(field) + { + std::vector v; + const auto &type = field.getType(); + if (type.getArraySize() && type.getArraySize() != c.size()) + { + QString sstr = "Wrong size in building value for " + field.getName() + ", got " + QString::number(c.size()) + " / expected " + + QString::number(type.getArraySize()); + throw std::logic_error(sstr.toStdString()); + } + v.reserve(c.size()); + for (auto val: c) + { + v.push_back(MakeStdAny(field, val)); + } + value = std::any(v); + }; + + /** + * TODO + * @tparam T + * @param field + * @param v + */ + template< + typename T, + typename = typename std::enable_if::value>::type + > + FieldValue(const MessageField &field, T v) : field(field) + { + value = MakeStdAny(field, v); + } + + /** + * TODO + * @tparam T + * @tparam Size + * @param c + */ + template< + typename T, + size_t Size> + void getValue(std::array &c) const + { + auto vec = std::any_cast < std::vector < std::any >> (value); + for (size_t i = 0; i < c.size(); ++i) + { + c[i] = std::any_cast(vec[i]); + } + } + + /** + * TODO + * @tparam Container + * @tparam T + * @param c + */ + template< + typename Container, + typename T = typename Container::value_type, + typename = typename std::enable_if< + !std::is_arithmetic::value && !std::is_same::value>::type + > + void getValue(Container &c) const + { + c.clear(); + auto vec = std::any_cast < std::vector < std::any >> (value); + for (auto val: vec) + { + c.push_back(std::any_cast(val)); + } + } + + /** + * TODO + * @tparam T + * @tparam E + * @param val + */ + template< + typename T, + typename E = typename std::enable_if::value>::type + > + void getValue(T &val) const + { + val = std::any_cast(value); + } + + /** + * TODO + * @tparam T + * @param val + */ + template::value>::type, + typename = typename std::enable_if::value>::type, + typename = typename T::value_type> + void getValue(T &val) const + { + val = std::any_cast(value); + } + + /** + * TODO + * @return + */ + [[nodiscard]] const MessageField &getField() const; + + /** + * TODO + * @return + */ + [[nodiscard]] const FieldType &getType() const; + + /** + * TODO + * @return + */ + [[nodiscard]] const QString &getName() const; + + /** + * TODO + * @return + */ + [[nodiscard]] const std::any &getValue() const; + + /** + * TODO + * @return + */ + [[nodiscard]] bool isOutputInt8AsInt() const; + + /** + * TODO + * @param outputInt8AsInt + */ + void setOutputInt8AsInt(bool outputInt8AsInt); + + /** + * + * @param buffer + * @return The number of bytes added + */ + size_t addToBuffer(BytesBuffer &buffer) const; + + /** + * + * @return the size of the field in bytes if stored in binary + */ + size_t getByteSize() const; + + private: + MessageField field; + std::any value; + bool output_int8_as_int=false; + + /** + * TODO + * @tparam ValueType + * @param field + * @param value + * @return + */ + template + static std::any MakeStdAny(const MessageField &field, const ValueType &value) + { + auto &type = field.getType(); + auto &name = field.getName(); + switch (type.getBaseType()) + { + case BaseType::NOT_A_TYPE: + throw std::logic_error("Field " + name.toStdString() + " as type NOT_A_TYPE"); + break; + case BaseType::CHAR: + return std::any(static_cast(value)); + case BaseType::INT8: + return std::any(static_cast(value)); + case BaseType::INT16: + return std::any(static_cast(value)); + case BaseType::INT32: + return std::any(static_cast(value)); + case BaseType::UINT8: + return std::any(static_cast(value)); + case BaseType::UINT16: + return std::any(static_cast(value)); + case BaseType::UINT32: + return std::any(static_cast(value)); + case BaseType::FLOAT: + return std::any(static_cast(value)); + case BaseType::STRING: + std::stringstream sstr; + sstr << value; + return std::any(QString::fromStdString(sstr.str())); + } + return std::any(); + } + }; +} +std::ostream& operator<<(std::ostream& o,const pprzlink::FieldValue& v); + +#endif //PPRZLINKCPP_FIELDVALUE_H diff --git a/pprzlinkQt/include/pprzlinkQt/IvyQtLink.h b/pprzlinkQt/include/pprzlinkQt/IvyQtLink.h new file mode 100644 index 0000000..4f87692 --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/IvyQtLink.h @@ -0,0 +1,55 @@ +#ifndef IVYQTLINK_H +#define IVYQTLINK_H + +#include +#include +#include + +class IvyQt; +class Peer; + +namespace pprzlink { + + using messageCallback_t = std::function; + + class IvyQtLink : public QObject + { + Q_OBJECT + public: + + explicit IvyQtLink(MessageDictionary const & dict , QString appName, QString domain = "127.255.255.255:2010", QObject *parent = nullptr); + ~IvyQtLink(); + + long BindMessage(MessageDefinition const & def, QObject* context, messageCallback_t cb); + void UnbindMessage(long bindId); + void sendMessage(const Message& msg); + long sendRequest(const Message& msg, messageCallback_t cb); + + signals: + + private: + const MessageDictionary &dictionary; + QString domain; + QString appName; + IvyQt* bus; + unsigned int requestNb; + boost::bimap requestBindId; + + void getMessageData(const Message& msg, QString &ac_id, QString &name, QString &fields); + + QString regexpForMessageDefinition(MessageDefinition const & def); + QString messageRegexp(MessageDefinition const & def); + void messageCallback(QStringList params, const messageCallback_t &cb); + + std::map> messagesCallbackMap; + //std::map aircraftCallbackMap; + //std::map requestCallbackMap; + + }; + + + + +} + +#endif // IVYQTLINK_H diff --git a/pprzlinkQt/include/pprzlinkQt/Message.h b/pprzlinkQt/include/pprzlinkQt/Message.h new file mode 100644 index 0000000..5ba5c37 --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/Message.h @@ -0,0 +1,204 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file Message.h + * + * + */ + +#ifndef PPRZLINKCPP_MESSAGE_H +#define PPRZLINKCPP_MESSAGE_H + +#include +#include +#include +#include +#include +#include + +namespace pprzlink { + /** + * + */ + class Message { + public: + + explicit Message(); + + /** + * + * @param def + */ + explicit Message(const MessageDefinition &def); + + /** + * + * @tparam ValueType + * @param name + * @param value + */ + template + void addField(const QString &name, ValueType value) + { + auto field = def.getField(name); // Will throw no_such_field if non existing field name + fieldValues[name] = FieldValue(field,value); + } + + /** + * + * @tparam ValueType + * @param name + * @param value + */ + template + void getField(const QString &name, ValueType &value) const + { + auto field = def.getField(name); // Will throw no_such_field if non existing field name + auto const &it = fieldValues.find(name); + if (it == fieldValues.end()) + { + throw field_has_no_value("In message " + def.getName().toStdString() + " field " + name.toStdString() + " has not value !"); + } + it->second.getValue(value); + } + + /** + * + * @tparam ValueType + * @param index + * @param value + */ + template + void getField(size_t index, ValueType &value) const + { + auto name = def.getField(index).getName(); + auto const &it = fieldValues.find(name); + if (it == fieldValues.end()) + { + throw field_has_no_value("In message " + def.getName() + " field " + name + " has not value !"); + } + it->second.getValue(value); + } + + /** + * + * @param index + * @param buffer + * @param offset + */ + void addFieldFromBuffer(size_t index, BytesBuffer const &buffer, size_t & offset); + + /** + * + * @param index + * @param buffer + * @return + */ + size_t addFieldToBuffer(size_t index, BytesBuffer &buffer) const; + + /** + * + * @param index + * @return + */ + [[nodiscard]] const FieldValue& getRawValue(int index) const; + + /** + * + * @param name + * @return + */ + [[nodiscard]] const FieldValue& getRawValue(const QString& name) const; + + /** + * + * @return + */ + [[nodiscard]] size_t getNbValues() const; + + /** + * + * @return + */ + [[nodiscard]] const MessageDefinition &getDefinition() const; + + /** + * + * @return + */ + [[nodiscard]] QString toString() const; + + /** + * + * @return + */ + const std::variant &getSenderId() const; + + /** + * + * @return + */ + uint8_t getReceiverId() const; + + /** + * + * @return + */ + uint8_t getComponentId() const; + + /** + * + * @return + */ + uint8_t getClassId() const; + + /** + * + * @param senderId + */ + void setSenderId(const std::variant &senderId); + + /** + * + * @param receiverId + */ + void setReceiverId(uint8_t receiverId); + + /** + * + * @param componentId + */ + void setComponentId(uint8_t componentId); + + /** + * + * @return the size of the message in bytes if stored in binary + */ + size_t getByteSize() const; + + private: + MessageDefinition def; + std::map fieldValues; + std::variant sender_id; + uint8_t receiver_id; + uint8_t component_id; + }; + +} +#endif //PPRZLINKCPP_MESSAGE_H diff --git a/pprzlinkQt/include/pprzlinkQt/MessageDefinition.h b/pprzlinkQt/include/pprzlinkQt/MessageDefinition.h new file mode 100644 index 0000000..2d192f5 --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/MessageDefinition.h @@ -0,0 +1,69 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageDefinition.h + * + * + */ + +#ifndef PPRZLINKCPP_MESSAGEDEFINITION_H +#define PPRZLINKCPP_MESSAGEDEFINITION_H + +#include +#include +#include +#include + +namespace pprzlink { + + class MessageDefinition { + public: + MessageDefinition (); + + explicit MessageDefinition (QDomElement xml,int classId); + + [[nodiscard]] uint8_t getClassId() const; + + [[nodiscard]] uint8_t getId() const; + + [[nodiscard]] const QString &getName() const; + + [[nodiscard]] size_t getNbFields() const; + + [[nodiscard]] const MessageField& getField(int index) const; + + [[nodiscard]] const MessageField& getField(const QString &name) const; + + [[nodiscard]] bool hasFieldName(const QString &name) const; + + [[nodiscard]] QString toString() const; + + [[nodiscard]] size_t getMinimumSize() const; + + [[nodiscard]] bool isRequest() const; + + private: + uint8_t classId; + uint8_t id; + QString name; + std::vector fields; + std::map fieldNameToIndex; + }; +} +#endif //PPRZLINKCPP_MESSAGEDEFINITION_H diff --git a/pprzlinkQt/include/pprzlinkQt/MessageDictionary.h b/pprzlinkQt/include/pprzlinkQt/MessageDictionary.h new file mode 100644 index 0000000..205b406 --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/MessageDictionary.h @@ -0,0 +1,56 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageDictionnary.h + * + * + */ + +#ifndef PPRZLINKCPP_MESSAGEDICTIONARY_H +#define PPRZLINKCPP_MESSAGEDICTIONARY_H + +#include +#include +#include + +namespace pprzlink { + class MessageDictionary { + public: + explicit MessageDictionary(QString const &fileName); + + [[nodiscard]] const MessageDefinition &getDefinition(QString const &name) const; + + [[nodiscard]] const MessageDefinition &getDefinition(int classId, int msgId) const; + + [[nodiscard]] std::pair getMessageId(QString name) const; + [[nodiscard]] QString getMessageName(int classId, int msgId) const; + + [[nodiscard]] int getClassId(QString name) const; + [[nodiscard]] QString getClassName(int id) const; + + [[nodiscard]] std::vector getMsgsForClass(QString className) const; + [[nodiscard]] std::vector getMsgsForClass(int classId) const; + + private: + std::map messagesDict; + boost::bimap> msgNameToId; + boost::bimap classMap; + }; +} +#endif //PPRZLINKCPP_MESSAGEDICTIONARY_H diff --git a/pprzlinkQt/include/pprzlinkQt/MessageField.h b/pprzlinkQt/include/pprzlinkQt/MessageField.h new file mode 100644 index 0000000..6c5cd9a --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/MessageField.h @@ -0,0 +1,52 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageField.h + * + * + */ + +#ifndef PPRZLINKCPP_MESSAGEFIELD_H +#define PPRZLINKCPP_MESSAGEFIELD_H + +#include +#include +#include + +namespace pprzlink { + + class MessageField { + public: + MessageField(const QString &name, const FieldType &type); + + MessageField(const QString &name, const QString &typeString); + + [[nodiscard]] const QString &getName() const; + + [[nodiscard]] const FieldType &getType() const; + + [[nodiscard]] size_t getSize() const; + private: + QString name; + FieldType type; + size_t size; + }; +} + +#endif //PPRZLINKCPP_MESSAGEFIELD_H diff --git a/pprzlinkQt/include/pprzlinkQt/MessageFieldTypes.h b/pprzlinkQt/include/pprzlinkQt/MessageFieldTypes.h new file mode 100644 index 0000000..921da39 --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/MessageFieldTypes.h @@ -0,0 +1,64 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageFieldTypes.h + * + * + */ + +#ifndef PPRZLINKCPP_MESSAGEFIELDTYPES_H +#define PPRZLINKCPP_MESSAGEFIELDTYPES_H + +#include + +namespace pprzlink { + + enum class BaseType { + NOT_A_TYPE, + CHAR, + INT8, + INT16, + INT32, + UINT8, + UINT16, + UINT32, + FLOAT, + STRING + }; + + size_t sizeofBaseType(BaseType type); + + class FieldType { + public: + explicit FieldType(QString const &typeString); + + [[nodiscard]] BaseType getBaseType() const; + + [[nodiscard]] bool isArray() const; + + [[nodiscard]] size_t getArraySize() const; + + [[nodiscard]] QString toString() const; + + private: + BaseType baseType; + int arraySize; // 0 for dynamic, -1 for not an array + }; +} +#endif //PPRZLINKCPP_MESSAGEFIELDTYPES_H diff --git a/pprzlinkQt/include/pprzlinkQt/old/IvyLink.cpp b/pprzlinkQt/include/pprzlinkQt/old/IvyLink.cpp new file mode 100644 index 0000000..8fe03fa --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/old/IvyLink.cpp @@ -0,0 +1,208 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file IvyLink.cpp + * + * + */ + + +#include +#include +#include +#include +#include + +namespace pprzlink { + + long IvyLink::BindOnSrcAc(std::string ac_id, messageCallback_t cb) + { + auto mcb = new AircraftCallback(dictionary, cb); + std::stringstream regexp; + regexp << "^(" << ac_id << ") " << "([^ ]*)( .*)?$"; + //std::cout << "Binding to " << regexp.str() << std::endl; + auto id = bus->BindMsg(regexp.str().c_str(), mcb); + aircraftCallbackMap[id] = mcb; + return id; + } + + long IvyLink::registerRequestAnswerer(const MessageDefinition &def, answererCallback_t cb) { + if(!def.isRequest()) { + throw message_is_not_request("Message " + def.getName() + " is not a request message. Use sendMessage instead!"); + } + + // remove last 4 characters (_REQ) from request name to get the answer message name + auto ansName = def.getName().substr(0, def.getName().size() - 4); + + std::stringstream regexpStream; + regexpStream << "^([^ ]*) ([^ ]*) " << messageRegexp(def); + + auto mcb = new RequestCallback(dictionary, [=](std::string ac_id,Message msg) { + auto answerMsg = cb(std::move(ac_id), std::move(msg)); + //check message name + if(ansName != answerMsg.getDefinition().getName()) { + throw wrong_answer_to_request("Wrong answer " + answerMsg.getDefinition().getName() + " to request " + def.getName()); + } + sendMessage(answerMsg); + }); + auto id = bus->BindMsg(regexpStream.str().c_str(), mcb); + requestCallbackMap[id] = mcb; + + return 0; + } + + + void RequestCallback::OnMessage(IvyApplication *app, int argc, const char **argv) { + (void)app; + + if (argc < 3) + { + throw bad_message_file("Not enough fields to be a valid request!"); + } + + requestId = std::string(argv[1]); + + MessageCallback::OnMessage(app, argc-1, argv+1); + } + + void AircraftCallback::OnMessage(IvyApplication *app, int argc, const char **argv) + { + (void)app; + MessageDefinition def = dictionary.getDefinition(argv[1]); + Message msg(def); + + if (argc==3) // If we have fields to parse + { + std::regex fieldRegex("([^ ]+|\"[^\"]+\")"); + + std::smatch results; + std::vector fields; + std::string fieldsStr(argv[2]); + while (std::regex_search(fieldsStr, results, fieldRegex)) + { + fields.push_back(results.str()); + fieldsStr = results.suffix(); + } + + if (def.getNbFields()!=fields.size()) // check that the number of fields is correct + { + throw wrong_message_format("Wrong number of fields in message " + std::string(argv[1])); + } + + for (size_t i = 0; i < def.getNbFields(); ++i) + { + const auto &field = def.getField(i); + + // For char arrays and strings remove possible quotes + if ((field.getType().getBaseType() == BaseType::STRING || + (field.getType().getBaseType() == BaseType::CHAR && field.getType().isArray())) && argv[i][0] == '"') + { + std::string &str(fields[i]); + //std::cout << str.substr(1,str.size()-2) << std::endl; + msg.addField(field.getName(), str.substr(1, str.size() - 2)); + } + else + { + std::stringstream sstr(fields[i]); + if (field.getType().isArray()) + { + switch (field.getType().getBaseType()) + { + case BaseType::NOT_A_TYPE: + throw std::logic_error("NOT_A_TYPE for field " + field.getName() + " in message " + argv[1]); + break; + case BaseType::CHAR: + throw wrong_message_format("Wrong field format for a char[] " + fields[i]); + break; + case BaseType::INT8: + case BaseType::INT16: + case BaseType::INT32: + case BaseType::UINT8: + case BaseType::UINT16: + case BaseType::UINT32: + case BaseType::FLOAT: + { + // Parse all numbers as a double + std::vector values; + while (!sstr.eof()) + { + double val; + char c; + sstr >> val >> c; + if (c != ',') + { + throw wrong_message_format("Wrong format for array \"" + fields[i] + "\""); + } + values.push_back(val); + } + msg.addField(field.getName(), values); // The value will be statically cast to the right type + } + break; + case BaseType::STRING: + msg.addField(field.getName(), fields[i]); + break; + } + } + else + { + switch (field.getType().getBaseType()) + { + case BaseType::NOT_A_TYPE: + throw std::logic_error("NOT_A_TYPE for field " + field.getName() + " in message " + argv[1]); + break; + case BaseType::CHAR: + { + char val; + sstr >> val; + msg.addField(field.getName(), val); + } + break; + case BaseType::INT8: + case BaseType::INT16: + case BaseType::INT32: + case BaseType::UINT8: + case BaseType::UINT16: + case BaseType::UINT32: + case BaseType::FLOAT: + { + // Parse all numbers as a double + double val; + sstr >> val; + msg.addField(field.getName(), val); // The value will be statically cast to the right type + } + break; + case BaseType::STRING: + msg.addField(field.getName(), fields[i]); + break; + } + } + } + } + } + std::string sender(argv[0]); + if (argv[0][0]=='"') + { + // Remove quotes from string if needed + sender=sender.substr(1,sender.size()-2); + } + msg.setSenderId(sender); + + cb(sender, msg); + } +} diff --git a/pprzlinkQt/include/pprzlinkQt/old/IvyLink.h b/pprzlinkQt/include/pprzlinkQt/old/IvyLink.h new file mode 100644 index 0000000..191b7ea --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/old/IvyLink.h @@ -0,0 +1,182 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file IvyLink.h + * + * + */ + + +#ifndef PPRZLINKCPP_IVYLINK_H +#define PPRZLINKCPP_IVYLINK_H + +#include +#include +#include +#include +#include + +namespace pprzlink { + class MessageCallback; + class AircraftCallback; + class RequestCallback; + + using messageCallback_t = std::function; + using answererCallback_t = std::function; + + /** + * + */ + class IvyLink : public IvyApplicationCallback { + public: + + /** + * + * @param def + * @param cb + * @return + */ + long BindMessage(MessageDefinition const & def, messageCallback_t cb); + + /** + * Bind on all messages coming from given ac_id + * @param ac_id + * @param cb + * @return + */ + long BindOnSrcAc(std::string ac_id, messageCallback_t cb); + + /** + * + * @param bindId + */ + void UnbindMessage(long bindId); + + /** + * + * @param ac_id + * @param msg + */ + void sendMessage(const Message& msg); + + long sendRequest(const Message& msg, messageCallback_t cb); + + long registerRequestAnswerer(const MessageDefinition &def, answererCallback_t cb); + + private: + const MessageDictionary &dictionary; + std::string domain; + std::string appName; + Ivy *bus; + bool threaded; + unsigned int requestNb; + boost::bimap requestBindId; + + void getMessageData(const Message& msg, std::string &ac_id, std::string &name, std::string &fieldStream); + + /** + * + * @param def + * @return + */ + std::string regexpForMessageDefinition(MessageDefinition const & def); + + std::string messageRegexp(MessageDefinition const & def); + + std::map messagesCallbackMap; + std::map aircraftCallbackMap; + std::map requestCallbackMap; + }; + + /** + * + */ + class MessageCallback : public IvyMessageCallback { + public: + /** + * + * @param dictionary + * @param cb + */ + MessageCallback(const MessageDictionary &dictionary,const messageCallback_t &cb); + + /** + * + * @param app + * @param argc + * @param argv + */ + void OnMessage(IvyApplication *app, int argc, const char **argv) override; + + private: + const MessageDictionary &dictionary; + messageCallback_t cb; + }; + + /** + * + */ + class AircraftCallback : public IvyMessageCallback { + public: + /** + * + * @param dictionary + * @param cb + */ + AircraftCallback(const MessageDictionary &dictionary,const messageCallback_t &cb); + + /** + * + * @param app + * @param argc + * @param argv + */ + void OnMessage(IvyApplication *app, int argc, const char **argv) override; + + private: + const MessageDictionary &dictionary; + messageCallback_t cb; + }; + + + class RequestCallback : public MessageCallback { + public: + /** + * + * @param dictionary + * @param cb + */ + RequestCallback(const MessageDictionary &dictionary,const messageCallback_t &cb); + + /** + * + * @param app + * @param argc + * @param argv + */ + void OnMessage(IvyApplication *app, int argc, const char **argv) override; + + std::string& getRequestId() {return requestId;} + + private: + std::string requestId; + }; + +} +#endif //PPRZLINKCPP_IVYLINK_H diff --git a/pprzlinkQt/include/pprzlinkQt/pprzlink_exception.h b/pprzlinkQt/include/pprzlinkQt/pprzlink_exception.h new file mode 100644 index 0000000..e83c58d --- /dev/null +++ b/pprzlinkQt/include/pprzlinkQt/pprzlink_exception.h @@ -0,0 +1,55 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file pprzlink_exception.h + * + * + */ + + +#ifndef PPRZLINKCPP_PPRZLINK_EXCEPTION_H +#define PPRZLINKCPP_PPRZLINK_EXCEPTION_H + +#include + +#define DECLARE_PPRZLINK_EXCEPT(a) class a : public pprzlink_exception {\ +public:\ +explicit a(const std::string &arg) :\ + pprzlink_exception(arg) {}\ +}; + +namespace pprzlink { + class pprzlink_exception : public std::runtime_error { + public: + pprzlink_exception(const std::string &arg) : runtime_error(arg){} + }; + + DECLARE_PPRZLINK_EXCEPT(wrong_message_format) + DECLARE_PPRZLINK_EXCEPT(no_such_message) + DECLARE_PPRZLINK_EXCEPT(no_such_field) + DECLARE_PPRZLINK_EXCEPT(field_has_no_value) + DECLARE_PPRZLINK_EXCEPT(no_such_class) + DECLARE_PPRZLINK_EXCEPT(messages_file_not_found) + DECLARE_PPRZLINK_EXCEPT(bad_message_file) + DECLARE_PPRZLINK_EXCEPT(no_such_binding) + DECLARE_PPRZLINK_EXCEPT(message_is_request) + DECLARE_PPRZLINK_EXCEPT(message_is_not_request) + DECLARE_PPRZLINK_EXCEPT(wrong_answer_to_request) +} +#endif //PPRZLINKCPP_PPRZLINK_EXCEPTION_H diff --git a/pprzlinkQt/src/FieldValue.cpp b/pprzlinkQt/src/FieldValue.cpp new file mode 100644 index 0000000..b5f897d --- /dev/null +++ b/pprzlinkQt/src/FieldValue.cpp @@ -0,0 +1,476 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file FieldValue.cpp + * + * + */ +#include +#include + +// FIXME This should go to a SERIALISER ! +std::ostream& operator<<(std::ostream& o,const pprzlink::FieldValue& v) +{ + const auto& type=v.getType(); + const auto& name=v.getName(); + if (type.isArray()) + { + switch (type.getBaseType()) + { + case pprzlink::BaseType::NOT_A_TYPE: + throw std::logic_error("NOT_A_TYPE for field "+name.toStdString()); + break; + case pprzlink::BaseType::CHAR: + { + std::vector vec; + v.getValue(vec); + o << "\""; + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i vec; + v.getValue(vec); + for (size_t i=0;i> (value); + if (getType().getArraySize()==0) // Variable length array + { + // TODO Check that the length is in number of elements not in bytes + buffer.push_back(vec.size()); // Add the length of the array in number of elements + } + for (auto elem : vec) + { + switch (getType().getBaseType()) + { + case BaseType::CHAR: + { + char val = std::any_cast(elem); + buffer.push_back(val); + } + break; + case BaseType::INT8: + { + char val = std::any_cast(elem); + buffer.push_back(val); + } + break; + case BaseType::INT16: + { + char val = std::any_cast(elem); + buffer.push_back(val & 0x00FFu); + buffer.push_back((val & 0xFF00u) >> 8u); + } + break; + case BaseType::INT32: + { + char val = std::any_cast(elem); + buffer.push_back(val & 0x000000FFu); + buffer.push_back((val & 0x0000FF00u) >> 8u); + buffer.push_back((val & 0x00FF0000u) >> 16u); + buffer.push_back((val & 0xFF000000u) >> 24u); + } + break; + case BaseType::UINT8: + { + char val = std::any_cast(elem); + buffer.push_back(val); + } + break; + case BaseType::UINT16: + { + char val = std::any_cast(elem); + buffer.push_back(val & 0x00FFu); + buffer.push_back((val & 0xFF00u) >> 8u); + } + break; + case BaseType::UINT32: + { + char val = std::any_cast(elem); + buffer.push_back(val & 0x000000FFu); + buffer.push_back((val & 0x0000FF00u) >> 8u); + buffer.push_back((val & 0x00FF0000u) >> 16u); + buffer.push_back((val & 0xFF000000u) >> 24u); + } + break; + case BaseType::FLOAT: + { + char val = std::any_cast(elem); + uint32_t *ptrval = (uint32_t *) &val; + buffer.push_back(*ptrval & 0x000000FFu); + buffer.push_back((*ptrval & 0x0000FF00u) >> 8u); + buffer.push_back((*ptrval & 0x00FF0000u) >> 16u); + buffer.push_back((*ptrval & 0xFF000000u) >> 24u); + } + break; + case BaseType::STRING: + throw std::logic_error("Cannot add an array of STRING to a buffer for field " + getName().toStdString()); + break; + case BaseType::NOT_A_TYPE: + throw std::logic_error("Cannot add a field of type NOT_A_TYPE to a buffer for field " + getName().toStdString()); + } + } + } + else // if (!getType().isArray()) + { + switch (getType().getBaseType()) + { + case BaseType::CHAR: + { + char val; + getValue(val); + buffer.push_back(val); + } + break; + case BaseType::INT8: + { + int8_t val; + getValue(val); + buffer.push_back(val); + } + break; + case BaseType::INT16: + { + int16_t val; + getValue(val); + buffer.push_back(val & 0x00FFu); + buffer.push_back((val & 0xFF00u) >> 8u); + } + break; + case BaseType::INT32: + { + int32_t val; + getValue(val); + buffer.push_back(val & 0x000000FFu); + buffer.push_back((val & 0x0000FF00u) >> 8u); + buffer.push_back((val & 0x00FF0000u) >> 16u); + buffer.push_back((val & 0xFF000000u) >> 24u); + } + break; + case BaseType::UINT8: + { + uint8_t val; + getValue(val); + buffer.push_back(val); + } + break; + case BaseType::UINT16: + { + uint16_t val; + getValue(val); + buffer.push_back(val & 0x00FFu); + buffer.push_back((val & 0xFF00u) >> 8u); + } + break; + case BaseType::UINT32: + { + uint32_t val; + getValue(val); + buffer.push_back(val & 0x000000FFu); + buffer.push_back((val & 0x0000FF00u) >> 8u); + buffer.push_back((val & 0x00FF0000u) >> 16u); + buffer.push_back((val & 0xFF000000u) >> 24u); + } + break; + case BaseType::FLOAT: + { + float val; + getValue(val); + uint32_t *ptrval = (uint32_t *) &val; + buffer.push_back(*ptrval & 0x000000FFu); + buffer.push_back((*ptrval & 0x0000FF00u) >> 8u); + buffer.push_back((*ptrval & 0x00FF0000u) >> 16u); + buffer.push_back((*ptrval & 0xFF000000u) >> 24u); + } + break; + case BaseType::STRING: + { + // A string is encoded as a variable char array (char[]) + QString val; + getValue(val); + buffer.push_back(val.length()); + for (auto c: val) + { + buffer.push_back(c.toLatin1()); + } + } + break; + case BaseType::NOT_A_TYPE: + throw std::logic_error("Cannot add a field of type NOT_A_TYPE to a buffer for field " + getName().toStdString()); + } + } + return buffer.size() - initialSize; + } + + size_t FieldValue::getByteSize() const + { + size_t elemSize = sizeofBaseType(getType().getBaseType()); + size_t size; + if (getType().getBaseType() == BaseType::STRING) + { + elemSize = 1; + } + if (getType().isArray()) + { + auto vec = std::any_cast>(value); + size= elemSize * vec.size(); + if (getType().getArraySize()==0) // If variable length array add one for the length + size++; + } + else if (getType().getBaseType() == BaseType::STRING)// String is considered as char[] + { + auto str = std::any_cast(value); + size= elemSize*str.length() +1; // String is a vraiable length array + } + else + { + size=elemSize; + } + + return size; + } +} diff --git a/pprzlinkQt/src/IvyQtLink.cpp b/pprzlinkQt/src/IvyQtLink.cpp new file mode 100644 index 0000000..72fb17a --- /dev/null +++ b/pprzlinkQt/src/IvyQtLink.cpp @@ -0,0 +1,331 @@ +#include +#include + +namespace pprzlink { + IvyQtLink::IvyQtLink(MessageDictionary const & dict , QString appName, QString domain, QObject *parent) : + QObject(parent), + dictionary (dict), domain(domain), appName(appName), requestNb(0) + { + bus = new IvyQt(appName, appName + " ready", this); + auto ip_port = domain.split(":"); + assert(ip_port.size() == 2); + bus->start(ip_port[0], ip_port[1].toUInt()); + } + + IvyQtLink::~IvyQtLink() + { + bus->stop(); + } + + long IvyQtLink::BindMessage(const MessageDefinition &def, QObject* context, messageCallback_t cb) + { + auto regexp = regexpForMessageDefinition(def); + //std::cout << "Binding to " << regexp << std::endl; + + auto mcb = [=](Peer* peer, QStringList params) { + (void)peer; + messageCallback(params, cb); + }; + + auto id = bus->bindMessage(regexp, context, mcb); + messagesCallbackMap[id] = mcb; + return id; + } + + void IvyQtLink::UnbindMessage(long bindId) + { + if (messagesCallbackMap.find(bindId) != messagesCallbackMap.end()) { + bus->unBindMessage(bindId); + messagesCallbackMap.erase(bindId); + return; + } + +// if (aircraftCallbackMap.find(bindId) != aircraftCallbackMap.end()) { +// bus->UnbindMsg(bindId); +// delete (aircraftCallbackMap[bindId]); +// aircraftCallbackMap.erase(bindId); +// return; +// } + +// if (requestCallbackMap.find(bindId) != requestCallbackMap.end()) { +// bus->UnbindMsg(bindId); +// delete (requestCallbackMap[bindId]); +// requestCallbackMap.erase(bindId); +// return; +// } + } + + QString IvyQtLink::messageRegexp(const MessageDefinition &def) + { + static const std::map typeRegex = { + {BaseType::CHAR, "."}, + {BaseType::INT8, "-?\\d+"}, + {BaseType::INT16, "-?\\d+"}, + {BaseType::INT32, "-?\\d+"}, + {BaseType::UINT8, "\\d+"}, + {BaseType::UINT16, "\\d+"}, + {BaseType::UINT32, "\\d+"}, + {BaseType::FLOAT, "-?\\d+(?:\\.)?(?:\\d)*"}, + {BaseType::STRING, "(?:\"[^\"]+\"|[^ ]+)"} + }; + // ac_id MSG_NAME msgField* + QString sstr = "(" + def.getName() + ")"; + + for (size_t i = 0; i < def.getNbFields(); ++i) + { + //std::cout << def.getField(i).getName() << " : " << def.getField(i).getType().toString() << std::endl; + auto iter = typeRegex.find(def.getField(i).getType().getBaseType()); + if (iter == typeRegex.end()) + { + throw wrong_message_format( + "IvyregexpForMessageDefinition found NOT_A_TYPE in message " + def.getField(i).getName().toStdString()); + } + QString baseRegex = iter->second; + + if (def.getField(i).getType().isArray()) + { + if (def.getField(i).getType().getBaseType()==BaseType::CHAR) + { + sstr += " (\"[^\"]*\")"; + } + else + { + if (def.getField(i).getType().getArraySize() == 0) + { + // Dynamic array + // s => s,s,s,s => (s(?:,s)*) + sstr += " (" + baseRegex + "(?:," + baseRegex + ")*)"; + } + else + { + // Static array + // s => s,s,s => (s(?:,s){SIZE-1}) + sstr += " (" + baseRegex + "(?:," + baseRegex + "){" + QString::number(def.getField(i).getType().getArraySize() - 1) + + "})"; + } + } + } + else + { + sstr += " (" + baseRegex + ")"; + } + } + sstr += "$"; + + return sstr; + } + + QString IvyQtLink::regexpForMessageDefinition(MessageDefinition const & def) { + QString sstr = "^([^ ]*) " + messageRegexp(def); + return sstr; + } + + void IvyQtLink::getMessageData(const Message& msg, QString &ac_id, QString &name, QString &fields) { + QString fieldsStream; + if (msg.getSenderId().index()==0) // The variant holds a string + { + ac_id = std::get(msg.getSenderId()); + } + else + { + + uint8_t ac_id_int = std::get(msg.getSenderId()); + ac_id = QString::number(ac_id_int); + } + const auto &def=msg.getDefinition(); + + for (size_t i=0;isend(message); + + } + + + long IvyQtLink::sendRequest(const Message& msg, messageCallback_t cb) { + const auto &def=msg.getDefinition(); + + if(!def.isRequest()) { + throw message_is_not_request("Message " + def.getName().toStdString() + " is not a request message. Use sendMessage instead!"); + } + + // remove last 4 characters (_REQ) from request name to get the answer message name + auto ansName = def.getName().mid(0, def.getName().size() - 4); + auto ansDef = dictionary.getDefinition(ansName); + + QString ac_id; + QString name; + QString fields; + + getMessageData(msg, ac_id, name, fields); + + QString requestId = QString::number(getpid()) + "_" + QString::number(requestNb++); + + + auto mcb = [=](Peer* peer, QStringList params) { + (void)peer; + messageCallback(params, [=](QString ac_id,Message msg) { + cb(std::move(ac_id), std::move(msg)); + + // find bind id, then unbind answer message + auto iter = requestBindId.left.find(requestId); + if (iter != requestBindId.left.end()) + { + long id = iter->second; + UnbindMessage(id); + requestBindId.left.erase(requestId); + + } + }); + }; + + + QString regexpStream = "^" + requestId + " ([^ ]*) " + messageRegexp(ansDef); + auto id = bus->bindMessage(regexpStream, mcb); + messagesCallbackMap[id] = mcb; + requestBindId.insert({requestId, id}); + QString message = ac_id + " " + requestId + " " + def.getName() + " " + fields; + bus->send(message); + + return id; + } + + void IvyQtLink::messageCallback(QStringList params, const messageCallback_t &cb) + { + MessageDefinition def = dictionary.getDefinition(params[1]); + Message msg(def); + if (def.getNbFields() != (size_t)(params.size() - 2) ) + { + QString sstr = params[1] + " message with wrong number of fields (expected " + def.getNbFields() + " / got " + QString::number(params.size()-2) + + ")"; + throw wrong_message_format(sstr.toStdString()); + } + for (int i = 2; i < params.size(); ++i) + { + const auto& field = def.getField(i - 2); + // Need deserializing string to build FieldValue + + // For char arrays and strings remove possible quotes + if ((field.getType().getBaseType()==BaseType::STRING || (field.getType().getBaseType()==BaseType::CHAR && field.getType().isArray())) && params[i][0]=='"') + { + QString str = params[i]; + //std::cout << str.substr(1,str.size()-2) << std::endl; + QString val = str.mid(1,str.size()-2); + msg.addField(field.getName(), val); + } + else + { + QString sstr(params[i]); + if (field.getType().isArray()) + { + switch (field.getType().getBaseType()) + { + case BaseType::NOT_A_TYPE: + throw std::logic_error("NOT_A_TYPE for field " + field.getName().toStdString() + " in message " + params[1].toStdString()); + break; + case BaseType::CHAR: + throw wrong_message_format("Wrong field format for a char[] " + params[i].toStdString()); + break; + case BaseType::INT8: + case BaseType::INT16: + case BaseType::INT32: + case BaseType::UINT8: + case BaseType::UINT16: + case BaseType::UINT32: + case BaseType::FLOAT: + { + // Parse all numbers as a double + std::vector values; + auto splitted = sstr.split(","); + for(auto ele: splitted) { + bool ok = false; + double val = ele.toDouble(&ok); + if(!ok) { + throw wrong_message_format("Wrong format for array "+params[i].toStdString()); + } + values.push_back(val); + } + msg.addField(field.getName(), values); // The value will be statically cast to the right type + } + break; + case BaseType::STRING: + msg.addField(field.getName(), params[i]); + break; + } + } + else + { + switch (field.getType().getBaseType()) + { + case BaseType::NOT_A_TYPE: + throw std::logic_error("NOT_A_TYPE for field " + field.getName().toStdString() + " in message " + params[1].toStdString()); + break; + case BaseType::CHAR: + { + char val; + val = sstr[0].toLatin1(); // TODO vérifier + msg.addField(field.getName(), val); + } + break; + case BaseType::INT8: + case BaseType::INT16: + case BaseType::INT32: + case BaseType::UINT8: + case BaseType::UINT16: + case BaseType::UINT32: + case BaseType::FLOAT: + { + // Parse all numbers as a double + double val; + val = sstr.toDouble(); + msg.addField(field.getName(), val); // The value will be statically cast to the right type + } + break; + case BaseType::STRING: + msg.addField(field.getName(), params[i]); + break; + } + } + } + } + QString sender(params[0]); + if (params[0][0]=='"') + { + // Remove quotes from string if needed + sender=sender.mid(1,sender.size()-2); + } + + cb(sender, msg); + } +} + + diff --git a/pprzlinkQt/src/Message.cpp b/pprzlinkQt/src/Message.cpp new file mode 100644 index 0000000..68f50d9 --- /dev/null +++ b/pprzlinkQt/src/Message.cpp @@ -0,0 +1,401 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file Message.cpp + * + * + */ + +#include +#include + +namespace pprzlink { + + Message::Message() : sender_id(static_cast(0)),receiver_id(static_cast(0)),component_id(static_cast(0)) + { + } + + Message::Message(const MessageDefinition &def) : def(def),sender_id(static_cast(0)),receiver_id(static_cast(0)),component_id(static_cast(0)) + { + } + + size_t Message::getNbValues() const + { + return fieldValues.size(); + } + + const MessageDefinition &Message::getDefinition() const + { + return def; + } + + template + void addFieldToStream(StreamType &stream,const MessageField &field,std::any value, bool uint8_as_int=false) + { + switch (field.getType().getBaseType()) + { + case BaseType::CHAR : + stream << std::any_cast(value); + break; + case BaseType::INT8: + if (uint8_as_int) + { + stream << (int)std::any_cast(value); + } + else + { + stream << std::any_cast(value); + } + break; + case BaseType::INT16: + stream << std::any_cast(value); + break; + case BaseType::INT32: + stream << std::any_cast(value); + break; + case BaseType::UINT8: + if (uint8_as_int) + { + stream << (int)std::any_cast(value); + } + else + { + stream << std::any_cast(value); + } + break; + case BaseType::UINT16: + stream << std::any_cast(value); + break; + case BaseType::UINT32: + stream << std::any_cast(value); + break; + case BaseType::FLOAT: + stream << std::any_cast(value); + break; + case BaseType::STRING: + stream << std::any_cast(value); + break; + case BaseType::NOT_A_TYPE: + // THIS SHOULD NOT HAPPEN !!!! + throw std::logic_error(QString(" field "+ field.getName() +"as type NOT_A_TYPE !").toStdString()); + break; + } + } + + template + void addVectorToStream([[maybe_unused]] StreamType &stream,const Message &msg,int index, [[maybe_unused]] bool uint8_as_int=false) + { + const auto& value = msg.getRawValue(index); + switch (value.getType().getBaseType()) + { + case BaseType::CHAR : + { + std::vector vec; + + } + break; + case BaseType::INT8: + break; + case BaseType::INT16: + break; + case BaseType::INT32: + break; + case BaseType::UINT8: + break; + case BaseType::UINT16: + break; + case BaseType::UINT32: + break; + case BaseType::FLOAT: + break; + case BaseType::STRING: + break; + case BaseType::NOT_A_TYPE: + // THIS SHOULD NOT HAPPEN !!!! + throw std::logic_error(" field " + value.getName().toStdString() + "as type NOT_A_TYPE !"); + break; + } + } + + QString Message::toString() const + { + QString sstr = def.getName() + " ["; + if (def.getNbFields()) + for (size_t i=0;isecond.getType().isArray() && found->second.getType().getBaseType()!=BaseType::CHAR) { + sstr += "{"; + } + auto val =found->second; + val.setOutputInt8AsInt(true); + std::stringstream ss; + ss << val; + sstr += QString::fromStdString(ss.str()); + if (found->second.getType().isArray() && found->second.getType().getBaseType()!=BaseType::CHAR) { + sstr += "}"; + } + } + } + sstr += "]"; + + return sstr; + } + + const FieldValue &Message::getRawValue(const QString &name) const + { + auto found = fieldValues.find(name); + if (found==fieldValues.end()) + { + throw pprzlink::no_such_field("No value for field "+name.toStdString()); + } + return found->second; + } + + const FieldValue &Message::getRawValue(int index) const + { + auto name = def.getField(index).getName(); + auto found = fieldValues.find(name); + if (found==fieldValues.end()) + { + throw pprzlink::no_such_field("No value for field "+name.toStdString()); + } + return found->second; + } + + const std::variant &Message::getSenderId() const + { + return sender_id; + } + + uint8_t Message::getReceiverId() const + { + return receiver_id; + } + + uint8_t Message::getComponentId() const + { + return component_id; + } + + uint8_t Message::getClassId() const + { + return def.getClassId(); + } + + void Message::setSenderId(const std::variant &senderId) + { + sender_id = senderId; + } + + void Message::setReceiverId(uint8_t receiverId) + { + receiver_id = receiverId; + } + + void Message::setComponentId(uint8_t componentId) + { + component_id = componentId; + } + + unsigned long long makeValue(BytesBuffer const &buffer, size_t offset, size_t elemSize) + { + unsigned long long value=0; + + for (size_t byteIndex=0; byteIndex < elemSize; ++byteIndex) + { + value |= buffer[offset + byteIndex] << (byteIndex * 8u); + } + return value; + } + + template + std::vector makeVector(BytesBuffer const &buffer, size_t offset, size_t nbElem, size_t elemSize) + { + std::vector vec; + for (size_t elemIndex=0; elemIndex < nbElem; ++elemIndex) + { + vec.push_back((BASE_TYPE)makeValue(buffer,offset+elemSize*elemIndex,elemSize)); + } + return vec; + } + size_t Message::addFieldToBuffer(size_t index, BytesBuffer &buffer) const + { + { + auto name = def.getField(index).getName(); + auto const &it = fieldValues.find(name); + if (it == fieldValues.end()) + { + throw field_has_no_value("In message " + def.getName().toStdString() + " field " + name.toStdString() + " has not value !"); + } + return it->second.addToBuffer(buffer); + } + } + + void Message::addFieldFromBuffer(size_t index, BytesBuffer const &buffer, size_t& offset) + { + auto const & field = getDefinition().getField(index); + auto const & fieldType = field.getType(); + auto size = field.getSize(); + unsigned long long value=0; + + // For arrays + if (fieldType.isArray()) + { + auto elemSize = sizeofBaseType(field.getType().getBaseType()); + if (size==0) // Variable length array + { + size = elemSize * makeValue(buffer,offset,1); // Read the length of the array + offset++; + } + switch (fieldType.getBaseType()) + { + case BaseType::CHAR: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + case BaseType::INT8: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + case BaseType::INT16: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + case BaseType::INT32: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + case BaseType::UINT8: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + case BaseType::UINT16: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + case BaseType::UINT32: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + case BaseType::FLOAT: + { + auto vec = makeVector(buffer,offset,size/elemSize,elemSize); + addField(field.getName(),vec); + break; + } + default: + throw std::logic_error("Type "+ fieldType.toString().toStdString() + " is not correct for PPRZ Transport."); + break; + } + offset+=size; + } + // If not an array and not a string (last case should not occur as strings are for Ivy only...) + else if (!fieldType.isArray() && size) + { + value = makeValue(buffer,offset,size); + offset+=size; + + switch (fieldType.getBaseType()) + { + case BaseType::CHAR: + addField(field.getName(),(char)value); + break; + case BaseType::INT8: + addField(field.getName(),(int8_t)value); + break; + case BaseType::INT16: + addField(field.getName(),(int16_t)value); + break; + case BaseType::INT32: + addField(field.getName(),(int32_t)value); + break; + case BaseType::UINT8: + addField(field.getName(),(uint8_t)value); + break; + case BaseType::UINT16: + addField(field.getName(),(uint16_t)value); + break; + case BaseType::UINT32: + addField(field.getName(),(uint32_t)value); + break; + case BaseType::FLOAT: + { + float *vf = (float*) &value; + addField(field.getName(), (float) *vf); + break; + } + case BaseType::STRING: + { + // A string is like a variable size array of char (char[]) + size = makeValue(buffer,offset,1); // Read the length of the string + offset++; + auto vec = makeVector(buffer,offset,size,1); + addField(field.getName(),vec); + break; + } + default: + throw std::logic_error("Type "+ fieldType.toString().toStdString()+ " is not correct for PPRZ Transport."); + break; + } + } + } + + size_t Message::getByteSize() const + { + size_t size= 0; + if (fieldValues.size() != getDefinition().getNbFields()) + { + throw pprzlink::field_has_no_value("Cannot get size of an incomplete message."); + } + for (auto value : fieldValues) + { + size+=value.second.getByteSize(); + } + + return size; + } +} diff --git a/pprzlinkQt/src/MessageDefinition.cpp b/pprzlinkQt/src/MessageDefinition.cpp new file mode 100644 index 0000000..95eb786 --- /dev/null +++ b/pprzlinkQt/src/MessageDefinition.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageDefinition.cpp + * + * + */ + +#include +#include +#include +#include + +namespace pprzlink { + MessageDefinition::MessageDefinition() + : classId(0), id(0) + { + } + + MessageDefinition::MessageDefinition(QDomElement xml, int classId) + : classId(classId), id(-1) + { + name = xml.attribute("name"); + id = xml.attribute("id").toUInt(); + + + for(auto field = xml.firstChildElement("field"); + !field.isNull(); + field = field.nextSiblingElement("field")) { + auto fieldName = field.attribute("name", ""); + auto fieldTypeStr = field.attribute("type", ""); + if (fieldName == "" || fieldTypeStr == "") + { + throw bad_message_file("Bad field"); + } + MessageField msgField(fieldName, fieldTypeStr); + + fieldNameToIndex[fieldName] = fields.size(); + fields.push_back(msgField); + } + + } + + uint8_t MessageDefinition::getClassId() const + { + return classId; + } + + uint8_t MessageDefinition::getId() const + { + return id; + } + + const QString &MessageDefinition::getName() const + { + return name; + } + + const MessageField &MessageDefinition::getField(int index) const + { + return fields[index]; + } + + const MessageField &MessageDefinition::getField(const QString &name) const + { + auto found =fieldNameToIndex.find(name); + if (found==fieldNameToIndex.end()) { + auto msg = QString("No field ") + name + QString(" in message ") + getName(); + throw no_such_field(msg.toStdString()); + } + return fields[found->second]; + } + + size_t MessageDefinition::getNbFields() const + { + return fields.size(); + } + + bool MessageDefinition::hasFieldName(const QString &name) const + { + return fieldNameToIndex.find(name)!=fieldNameToIndex.end(); + } + + QString MessageDefinition::toString() const + { + QString sstr; + sstr += getName() + "(" + QString::number(getId()) + ") in class " + QString::number(getClassId()) + "\n"; + for (size_t i=0;i= req.length()) { + return (name.right(req.length()) == req); + } else { + return false; + } + } +} diff --git a/pprzlinkQt/src/MessageDictionary.cpp b/pprzlinkQt/src/MessageDictionary.cpp new file mode 100644 index 0000000..c00a9b0 --- /dev/null +++ b/pprzlinkQt/src/MessageDictionary.cpp @@ -0,0 +1,189 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageDictionnary.cpp + * + * + */ + +#include +#include +#include + +// TODO Implement the dictionnary as a singleton ! + +namespace pprzlink { + + MessageDictionary::MessageDictionary(QString const &fileName) + { + + QDomDocument xml; + QFile f(fileName); + if(!f.open(QIODevice::ReadOnly)) { + throw std::runtime_error("Error while loading layout file"); + } + xml.setContent(&f); + f.close(); + + QDomElement rootElem = xml.documentElement(); + + if(rootElem.tagName() !="protocol") + { + throw bad_message_file("Root element is not protocol in xml messages file (found "+rootElem.tagName().toStdString()+")."); + } + + + for(auto msg_class = rootElem.firstChildElement("msg_class"); + !msg_class.isNull(); + msg_class = msg_class.nextSiblingElement("msg_class")) { + + auto className = msg_class.attribute("name", ""); + int classId = msg_class.attribute("id", "-1").toInt(); + if (className == "" || classId == -1) + { + throw bad_message_file(fileName.toStdString() + " msg_class as no name or id."); + } + classMap.left.insert(boost::bimap::left_value_type(classId,className)); + + for(auto message = msg_class.firstChildElement("message"); + !message.isNull(); + message = message.nextSiblingElement("message")) { + auto messageName = message.attribute("name", ""); + int messageId = message.attribute("id", "-1").toInt(); + if (messageName == "" || messageId == -1) + { + throw bad_message_file(fileName.toStdString() + " in class : " + className.toStdString() + " message as no name or id."); + } + try + { + MessageDefinition def(message, classId); + messagesDict[messageName]=def; + msgNameToId.left.insert(boost::bimap>::left_value_type(messageName,std::make_pair(classId,messageId))); + } catch (bad_message_file &e) + { + throw bad_message_file(QString(fileName + " in class : " + className + " message " + messageName + " has a bad field.").toStdString()); + } + } + } + } + + const MessageDefinition &MessageDictionary::getDefinition(QString const & name) const + { + auto iter = messagesDict.find(name); + if (iter == messagesDict.end()) + { + QString sstr = "could not find message with name " + name; + throw no_such_message(sstr.toStdString()); + } + else + { + return iter->second; + } + } + + const MessageDefinition &MessageDictionary::getDefinition(int classId, int msgId) const + { + auto iter = msgNameToId.right.find(std::make_pair(classId, msgId)); + if (iter == msgNameToId.right.end()) + { + QString sstr = "could not find message with id (" + QString::number(classId) + ":" + QString::number(msgId) + ")"; + throw no_such_message(sstr.toStdString()); + } + else + { + QString name = iter->second; + //std::cout << "message with id (" << classId << ":" << msgId << ") = " << name << std::endl; + //std::cout << messagesDict.find(name)->second.toString() << std::endl; + return messagesDict.find(name)->second; + } + } + + std::pair MessageDictionary::getMessageId(QString name) const + { + auto iter = msgNameToId.left.find(name); + if (iter == msgNameToId.left.end()) + { + throw no_such_message("No message with name " + name.toStdString()); + } + else + { + return iter->second; + } + } + + QString MessageDictionary::getMessageName(int classId, int msgId) const + { + auto iter = msgNameToId.right.find(std::make_pair(classId, msgId)); + if (iter == msgNameToId.right.end()) + { + QString sstr = "could not find message with id (" + QString::number(classId) + ":" + QString::number(msgId) + ")"; + throw no_such_message(sstr.toStdString()); + } + else + { + return iter->second; + } + } + + int MessageDictionary::getClassId(QString name) const + { + auto iter = classMap.right.find(name); + if (iter == classMap.right.end()) + { + QString sstr = "could not find class named " + name; + throw no_such_class(sstr.toStdString()); + } + else + { + return iter->second; + } + } + + QString MessageDictionary::getClassName(int id) const + { + auto iter = classMap.left.find(id); + if (iter == classMap.left.end()) + { + QString sstr = "could not find class with id " + QString::number(id);; + throw no_such_class(sstr.toStdString()); + } + else + { + return iter->second; + } + } + + std::vector MessageDictionary::getMsgsForClass(QString className) const + { + return getMsgsForClass(getClassId(className)); + } + + std::vector MessageDictionary::getMsgsForClass(int classId) const + { + std::vector result; + for (auto msgPair : messagesDict) + { + auto &def = msgPair.second; + if (def.getClassId()==classId) + result.push_back(def); + } + return result; + } +} + diff --git a/pprzlinkQt/src/MessageField.cpp b/pprzlinkQt/src/MessageField.cpp new file mode 100644 index 0000000..36f6f5c --- /dev/null +++ b/pprzlinkQt/src/MessageField.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageField.cpp + * + * + */ + +#include +#include + +namespace pprzlink { + + MessageField::MessageField(const QString &name, const FieldType &type) + : name(name), type(type), size(0) + { + if (getType().getBaseType()==BaseType::STRING) + size = 0; + else if (getType().isArray()) + size=sizeofBaseType(getType().getBaseType())*getType().getArraySize(); + else + size=sizeofBaseType(getType().getBaseType()); + } + + MessageField::MessageField(const QString &name, const QString &typeString) + : name(name), type(typeString), size(0) + { + if (getType().getBaseType()==BaseType::STRING) + size = 0; + else if (getType().isArray()) + size+=sizeofBaseType(getType().getBaseType())*getType().getArraySize(); + else + size+=sizeofBaseType(getType().getBaseType()); + } + + const QString &MessageField::getName() const + { + return name; + } + + const FieldType &MessageField::getType() const + { + return type; + } + + size_t MessageField::getSize() const + { + return size; + } +} diff --git a/pprzlinkQt/src/MessageFieldTypes.cpp b/pprzlinkQt/src/MessageFieldTypes.cpp new file mode 100644 index 0000000..14d0815 --- /dev/null +++ b/pprzlinkQt/src/MessageFieldTypes.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2019 garciafa + * This file is part of PprzLinkCPP + * + * PprzLinkCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PprzLinkCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with ModemTester. If not, see . + * + */ + +/** \file MessageFieldTypes.cpp + * + * + */ + +#include +#include +#include +#include +#include + +namespace pprzlink { + + const std::map sizeofTypeMap = { + {BaseType::CHAR, 1}, + {BaseType::INT8, 1}, + {BaseType::INT16, 2}, + {BaseType::INT32, 4}, + {BaseType::UINT8, 1}, + {BaseType::UINT16, 2}, + {BaseType::UINT32, 4}, + {BaseType::FLOAT, 4}, + {BaseType::STRING, 0} + }; + + static const std::map typeMap{ + {BaseType::CHAR, "char"}, + {BaseType::INT8, "int8"}, + {BaseType::INT16, "int16"}, + {BaseType::INT32, "int32"}, + {BaseType::UINT8, "uint8"}, + {BaseType::UINT16, "uint16"}, + {BaseType::UINT32, "uint32"}, + {BaseType::FLOAT, "float"}, + {BaseType::STRING, "string"} + }; + + FieldType::FieldType(QString const &typeString) + : baseType(BaseType::NOT_A_TYPE), arraySize(-1) + { + for (const auto& pairs : typeMap) + { + if (typeString.contains(pairs.second)) + { + baseType = pairs.first; + break; + } + } + if (baseType==BaseType::NOT_A_TYPE) + throw pprzlink::bad_message_file("Field with type string "+typeString.toStdString()+ " resolved to NOT_A_TYPE"); + auto openSquareBracketPos=typeString.indexOf('['); + if (openSquareBracketPos != -1) // This is an array + { + auto closeSquareBracketPos=typeString.indexOf(']'); + if (openSquareBracketPos==closeSquareBracketPos-1) + { + // Dynamic array + arraySize=0; + } + else + { + QString sstr = typeString.mid(openSquareBracketPos+1, closeSquareBracketPos-openSquareBracketPos-1); + arraySize = sstr.toInt(); + } + } + } + + BaseType FieldType::getBaseType() const + { + return baseType; + } + + bool FieldType::isArray() const + { + return arraySize != -1; + } + + size_t FieldType::getArraySize() const + { + return arraySize; + } + + QString FieldType::toString() const + { + QString sstr; + auto baseTypeStr = typeMap.find(baseType)->second; + sstr += baseTypeStr; + if (arraySize>0) + { + sstr += '[' + QString::number(arraySize) + ']'; + } + else if (arraySize==0) + { + sstr += "[]"; + } + return sstr; + } + + size_t sizeofBaseType(BaseType type) + { + if (type==BaseType::NOT_A_TYPE) + throw std::logic_error("Type NOT_A_TYPE in sizeofBaseType"); + + return sizeofTypeMap.find(type)->second; + } +}