Continues migration with DDS, Common
This commit is contained in:
1 parent
9b15cee841
commit
fa48db111e
668 files changed
+111841
-2
No files matched your search
+68
@@ -0,0 +1,68 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(SRCS_hdr_only
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data_modification_enum.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dds_bitarray.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dds_vector.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/connection.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/json_object.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/outdated_position.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qbytearray_byteaccess.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qpoint3d.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/await.h
|
||||
)
|
||||
ADD_LIBRARY(LtsUtilsCoreHeaderOnly INTERFACE)
|
||||
TARGET_INCLUDE_DIRECTORIES(LtsUtilsCoreHeaderOnly
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
TARGET_SOURCES(LtsUtilsCoreHeaderOnly INTERFACE ${SRCS_hdr_only})
|
||||
|
||||
SET(SRCS
|
||||
pgarray.cpp
|
||||
qplane3d.cpp
|
||||
qpoint3d.cpp
|
||||
scoped_connection.cpp
|
||||
sfm_bytemessage_converter.cpp
|
||||
promise.cpp
|
||||
lts_translatable_identifier.cpp
|
||||
lts_portrange.cpp
|
||||
#variant_operators.h
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(ALL LtsUtilsCore SHARED ${SRCS})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(LtsUtilsCore
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(LtsUtilsCore
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
LtsUtilsCoreHeaderOnly
|
||||
)
|
||||
|
||||
#
|
||||
# --- Tests --------------------------------------------------------------
|
||||
#
|
||||
# IF (TARGET ddscore) // does not work, not yet included
|
||||
IF((BUILD_WS OR BUILD_SV) AND BUILD_TESTING)
|
||||
ADD_EXECUTABLE(utils_test
|
||||
UnitTests/utils_test.cpp
|
||||
UnitTests/utils_test.h
|
||||
promise_examples.h
|
||||
promise_examples.cpp
|
||||
)
|
||||
TARGET_LINK_LIBRARIES(utils_test
|
||||
LtsUtilsCore
|
||||
Qt5::Core
|
||||
Qt5::Test
|
||||
ddscore
|
||||
)
|
||||
ADD_TEST(NAME utils_test COMMAND $<TARGET_FILE:utils_test>)
|
||||
ENDIF()
|
||||
+1202
File diff suppressed because it is too large.
Load diff
+62
@@ -0,0 +1,62 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class LtsUtilsTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
|
||||
void test_toIntArray();
|
||||
void test_fromIntArray();
|
||||
void test_toBigintArray();
|
||||
void test_fromBigintArray();
|
||||
void test_toIntArray2D();
|
||||
void test_fromIntArray2D();
|
||||
void test_toBigintArray2D();
|
||||
void test_fromBigintArray2D();
|
||||
|
||||
void test_QPoint3D();
|
||||
|
||||
|
||||
void test_bitArray();
|
||||
|
||||
|
||||
void test_convert();
|
||||
|
||||
|
||||
void test_connection();
|
||||
void test_queuedConnection();
|
||||
void test_signalSignalConnection();
|
||||
void test_danglingConnection1();
|
||||
void test_danglingConnection2();
|
||||
void test_danglingConnection3();
|
||||
|
||||
|
||||
void test_jsonObject_fromJson();
|
||||
void test_jsonObject_contains();
|
||||
void test_jsonObject_value();
|
||||
void test_jsonObject_valueWithError();
|
||||
void test_jsonObject_valueOr();
|
||||
void test_jsonObject_integers();
|
||||
void test_jsonObject_nullValues();
|
||||
void test_jsonArray_fromJson();
|
||||
void test_jsonArray();
|
||||
void test_jsonArray_nullArray();
|
||||
|
||||
|
||||
void test_byteStreamFromSfmMessage();
|
||||
void test_byteStreamFromSfmMessage2();
|
||||
void test_byteStreamToSfmMessage();
|
||||
|
||||
|
||||
void test_promise();
|
||||
void test_promise_example1();
|
||||
void test_promise_example2();
|
||||
void test_promise_example3();
|
||||
|
||||
|
||||
void byteAccess();
|
||||
};
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QEventLoop>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
template <typename Func>
|
||||
void addConnect(QEventLoop &x, bool &success, const typename QtPrivate::FunctionPointer<Func>::Object *sender, Func signal, bool isSuccess)
|
||||
{
|
||||
if (isSuccess)
|
||||
{
|
||||
QObject::connect(sender, signal, &x, [&success]()
|
||||
{
|
||||
|
||||
success = true;
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(sender, signal, &x, &QEventLoop::quit, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
template <typename Func, typename... Connect>
|
||||
void addConnect(QEventLoop &x, bool &success, const typename QtPrivate::FunctionPointer<Func>::Object *sender, Func signal, bool isSuccess, Connect... connects)
|
||||
{
|
||||
addConnect(x, success, sender, signal, isSuccess);
|
||||
addConnect(x, success, connects...);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Connect>
|
||||
void await(Connect... connects) noexcept(false)
|
||||
{
|
||||
QEventLoop x;
|
||||
bool success = false;
|
||||
|
||||
addConnect(x, success, connects...);
|
||||
|
||||
|
||||
x.exec();
|
||||
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
||||
throw std::runtime_error("Failed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename... Connect>
|
||||
void awaitTimeout(const int timeoutMs, Connect... connects) noexcept(false)
|
||||
{
|
||||
std::unique_ptr<QTimer> t(new QTimer);
|
||||
bool timeout = false;
|
||||
|
||||
if (timeoutMs > -1)
|
||||
{
|
||||
t->setInterval(timeoutMs);
|
||||
t->setSingleShot(true);
|
||||
QObject::connect(t.get(), &QTimer::timeout, t.get(), [&timeout](){ fprintf(stderr, "awaitTimeout: timed out!\n"); timeout = true; });
|
||||
t->start();
|
||||
}
|
||||
|
||||
await(t.get(), &QTimer::timeout, false, connects...);
|
||||
|
||||
|
||||
if (timeout)
|
||||
{
|
||||
|
||||
throw std::runtime_error("Timeout.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename Func1, typename Func2>
|
||||
void awaitWithCancelationSignal(const typename QtPrivate::FunctionPointer<Func1>::Object *sender1, Func1 signal1,
|
||||
const typename QtPrivate::FunctionPointer<Func2>::Object *sender2, Func2 signal2,
|
||||
const int timeoutMs = 0) noexcept(false)
|
||||
{
|
||||
if (timeoutMs > 0)
|
||||
{
|
||||
awaitTimeout(timeoutMs,
|
||||
sender1, signal1, true,
|
||||
sender2, signal2, false);
|
||||
return;
|
||||
}
|
||||
|
||||
await(sender1, signal1, true,
|
||||
sender2, signal2, false);
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QMetaObject>
|
||||
#include <QPointer>
|
||||
#include <QtGlobal>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
|
||||
|
||||
class IConnection
|
||||
{
|
||||
public:
|
||||
virtual ~IConnection() = default;
|
||||
virtual void connect(Qt::ConnectionType = Qt::AutoConnection) = 0;
|
||||
virtual void disconnect() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class DummyConnection : public IConnection
|
||||
{
|
||||
public:
|
||||
void connect(Qt::ConnectionType = Qt::AutoConnection) override {}
|
||||
void disconnect() override {}
|
||||
};
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
struct ExtractClass;
|
||||
|
||||
|
||||
|
||||
template<class T, class Member>
|
||||
struct ExtractClass<Member T::*>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class Signal, class Slot>
|
||||
class Connection : public IConnection
|
||||
{
|
||||
static_assert(std::is_member_function_pointer<Signal>::value, "Must be a pointer to method");
|
||||
static_assert(std::is_member_function_pointer<Slot>::value, "Must be a pointer to method");
|
||||
|
||||
using Emitter = typename detail::ExtractClass<Signal>::type;
|
||||
using Target = typename detail::ExtractClass<Slot>::type;
|
||||
|
||||
static_assert(std::is_base_of<QObject, Emitter>::value, "Must be derived from QObject");
|
||||
static_assert(std::is_base_of<QObject, Target>::value, "Must be derived from QObject");
|
||||
|
||||
public:
|
||||
Connection(Emitter *emitter, Signal signal, Target *target, Slot slot):
|
||||
m_emitter(emitter),
|
||||
m_signal(signal),
|
||||
m_target(target),
|
||||
m_slot(slot)
|
||||
{
|
||||
Q_ASSERT(emitter);
|
||||
Q_ASSERT(signal);
|
||||
Q_ASSERT(target);
|
||||
Q_ASSERT(slot);
|
||||
}
|
||||
|
||||
void connect(Qt::ConnectionType connectionType = Qt::AutoConnection) override
|
||||
{
|
||||
Q_ASSERT(!m_connected);
|
||||
|
||||
m_connected = true;
|
||||
|
||||
|
||||
|
||||
if (!m_emitter || !m_target)
|
||||
return;
|
||||
|
||||
m_conn = QObject::connect(m_emitter.data(), m_signal, m_target.data(), m_slot, connectionType);
|
||||
Q_ASSERT(m_conn);
|
||||
}
|
||||
|
||||
void disconnect() override
|
||||
{
|
||||
Q_ASSERT(m_connected);
|
||||
|
||||
if (m_conn)
|
||||
QObject::disconnect(m_conn);
|
||||
|
||||
m_connected = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_connected{false};
|
||||
QPointer<Emitter> m_emitter;
|
||||
Signal m_signal;
|
||||
QPointer<Target> m_target;
|
||||
Slot m_slot;
|
||||
QMetaObject::Connection m_conn;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class Signal, class Slot>
|
||||
Connection<Signal, Slot>* createConnection(typename detail::ExtractClass<Signal>::type *emitter, Signal signal,
|
||||
typename detail::ExtractClass<Slot>::type *target, Slot slot)
|
||||
{
|
||||
return new Connection<Signal, Slot>(emitter, signal, target, slot);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
enum class DataModification
|
||||
{
|
||||
eAdded,
|
||||
eModified,
|
||||
eRemoved
|
||||
};
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &str, DataModification mod)
|
||||
{
|
||||
switch(mod)
|
||||
{
|
||||
case DataModification::eAdded:
|
||||
str << "DataModification::eAdded";
|
||||
break;
|
||||
case DataModification::eModified:
|
||||
str << "DataModification::eModified";
|
||||
break;
|
||||
case DataModification::eRemoved:
|
||||
str << "DataModification::eRemoved";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef SUPPORT_QBITARRAY
|
||||
# include <QtCore/QBitArray>
|
||||
#endif
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
namespace BitArray
|
||||
{
|
||||
constexpr inline int bitNumber(int position)
|
||||
{
|
||||
return 7 - (position % 8);
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool testBit(const T &bitField, int position, bool *oob = nullptr)
|
||||
{
|
||||
const size_t byteNum = position / 8;
|
||||
if (byteNum >= bitField.size())
|
||||
{
|
||||
if (oob)
|
||||
*oob = true;
|
||||
return false;
|
||||
}
|
||||
if (oob)
|
||||
*oob = false;
|
||||
|
||||
const typename T::value_type mask = 1 << bitNumber(position);
|
||||
return ((bitField.at(byteNum) & mask) == mask);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool setBit(T &bitField, int position, bool set = true)
|
||||
{
|
||||
const size_t byteNum = position / 8;
|
||||
if (byteNum >= bitField.size())
|
||||
return false;
|
||||
|
||||
const typename T::value_type mask = 1 << bitNumber(position);
|
||||
if (set)
|
||||
bitField[byteNum] |= mask;
|
||||
else
|
||||
bitField[byteNum] &= ~mask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool clearBit(T &bitField, int position)
|
||||
{
|
||||
return setBit(bitField, position, false);
|
||||
}
|
||||
|
||||
|
||||
#ifdef SUPPORT_QBITARRAY
|
||||
template<typename T>
|
||||
inline T convert(const QBitArray &bitArray)
|
||||
{
|
||||
T ret;
|
||||
ret.resize((bitArray.size() + 7) / 8, 0);
|
||||
|
||||
for (int i = 0; i < bitArray.size(); ++i)
|
||||
{
|
||||
const size_t byteNum = i / 8;
|
||||
const typename T::value_type mask = (1 << bitNumber(i));
|
||||
if (bitArray[i])
|
||||
ret[byteNum] |= mask;
|
||||
else
|
||||
ret[byteNum] &= ~mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QBitArray convert(T &bitField, int realSize)
|
||||
{
|
||||
QBitArray ba(realSize);
|
||||
for (int32_t i = 0; i < realSize; ++i)
|
||||
{
|
||||
const size_t byteNum = i / 8;
|
||||
const typename T::value_type mask = (1 << bitNumber(i));
|
||||
bool bit = ((bitField[byteNum] & mask) == mask);
|
||||
ba[i] = bit;
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
template <typename T, typename S>
|
||||
inline void convert(const S &in, T &out)
|
||||
{
|
||||
static_assert(!std::is_same<S, T>::value, "Try to convert a type to the same type - is useless");
|
||||
out.reserve(static_cast<int>(in.size()));
|
||||
std::copy(in.begin(), in.end(), std::back_inserter(out));
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
inline T convert(const S &in)
|
||||
{
|
||||
static_assert(!std::is_same<S, T>::value, "Try to convert a type to the same type - is useless");
|
||||
T out;
|
||||
convert(in, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline QByteArray toQByteArray(const T &in)
|
||||
{
|
||||
return convert<QByteArray>(in);
|
||||
}
|
||||
|
||||
template <typename T, typename V = typename T::value_type>
|
||||
bool isInVector(const T &vec, const V &valueToSearch)
|
||||
{
|
||||
const auto end = vec.end();
|
||||
return std::find(vec.begin(), end, valueToSearch) != end;
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
class QByteArray;
|
||||
class QPixmap;
|
||||
class QImage;
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
LTSUTILSCORE_EXPORT QPixmap getPixmap(const std::vector<uint8_t> &data);
|
||||
LTSUTILSCORE_EXPORT QPixmap getPixmap(const QByteArray &data);
|
||||
LTSUTILSCORE_EXPORT QImage getImage(const std::vector<uint8_t> &data);
|
||||
LTSUTILSCORE_EXPORT QImage getImage(const QByteArray &data);
|
||||
LTSUTILSCORE_EXPORT std::vector<uint8_t> fromPixmap(const QPixmap &pixmap, bool *bIsPng = nullptr);
|
||||
LTSUTILSCORE_EXPORT std::vector<uint8_t> fromImage(const QImage &image, bool *bIsPng = nullptr);
|
||||
|
||||
}
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
#include <LtsUtilsCore/json_object.h>
|
||||
+696
@@ -0,0 +1,696 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonValue>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <exception>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
class JsonParseError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
JsonParseError(const QJsonParseError &err)
|
||||
: std::runtime_error("json parse error: " + err.errorString().toStdString() + " - at offset " + std::to_string(err.offset)){}
|
||||
};
|
||||
|
||||
class JsonError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class JsonKeyError : public JsonError
|
||||
{
|
||||
public:
|
||||
using JsonError::JsonError;
|
||||
};
|
||||
|
||||
class JsonRangeError : public JsonError
|
||||
{
|
||||
public:
|
||||
using JsonError::JsonError;
|
||||
};
|
||||
|
||||
class JsonTypeError : public JsonError
|
||||
{
|
||||
public:
|
||||
using JsonError::JsonError;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class JsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
class Key;
|
||||
|
||||
JsonObject() = default;
|
||||
explicit JsonObject(const QJsonObject &json)
|
||||
: m_json(json)
|
||||
, m_jsonIsSet(true)
|
||||
{}
|
||||
|
||||
explicit JsonObject(QJsonObject &&json)
|
||||
: m_json(std::move(json))
|
||||
, m_jsonIsSet(true)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static inline JsonObject fromJson(const char *json, int len = -1);
|
||||
static inline JsonObject fromJson(const QByteArray &json) { return fromJson(json.data(), json.size()); }
|
||||
static inline JsonObject fromJson(const std::string &json) { return fromJson(json.c_str(), json.size()); }
|
||||
|
||||
static inline JsonObject fromJsonSafe(const char *json, int len = -1) noexcept(false);
|
||||
static inline JsonObject fromJsonSafe(const QByteArray &json) noexcept(false) { return fromJsonSafe(json.data(), json.size()); }
|
||||
static inline JsonObject fromJsonSafe(const std::string &json) noexcept(false) { return fromJsonSafe(json.c_str(), json.size()); }
|
||||
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
bool contains(const Key &key) const;
|
||||
|
||||
bool isNull(const Key &key) const { return contains<void>(key); }
|
||||
|
||||
bool isEmpty() const { return m_json.isEmpty(); }
|
||||
bool isValid() const { return m_jsonIsSet; }
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
T value(const Key &key) const noexcept(false);
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
T valueOr(const Key &key, const T &defaultValue) const noexcept(false);
|
||||
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
bool isMap() const;
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
QMap<QString, T> toMap() const noexcept(false);
|
||||
|
||||
|
||||
template<class T>
|
||||
std::map<std::string, T> toStdMap() const;
|
||||
|
||||
bool operator==(const JsonObject &other) const { return m_json == other.m_json; }
|
||||
bool operator!=(const JsonObject &other) const { return !(*this == other); }
|
||||
|
||||
class Key
|
||||
{
|
||||
public:
|
||||
Key(const QString &s)
|
||||
: m_s(s)
|
||||
{}
|
||||
|
||||
Key(const std::string &s)
|
||||
: m_s(QString::fromStdString(s))
|
||||
{}
|
||||
|
||||
Key(const char *s)
|
||||
: m_s(QString::fromUtf8(s))
|
||||
{}
|
||||
|
||||
operator const QString &() const { return m_s; }
|
||||
|
||||
private:
|
||||
const QString m_s;
|
||||
};
|
||||
|
||||
operator const QJsonObject &() const { return m_json; }
|
||||
const QJsonObject &object() const { return m_json; }
|
||||
|
||||
private:
|
||||
bool containsAny(const QString &key) const { return m_json.contains(key); }
|
||||
|
||||
QJsonObject m_json;
|
||||
bool m_jsonIsSet = false;
|
||||
};
|
||||
|
||||
JsonObject JsonObject::fromJson(const char *json, int len)
|
||||
{
|
||||
if (len < 0)
|
||||
len = strlen(json);
|
||||
QJsonParseError error;
|
||||
const auto doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
return JsonObject();
|
||||
|
||||
if (!doc.isObject())
|
||||
return JsonObject();
|
||||
|
||||
return JsonObject(doc.object());
|
||||
}
|
||||
|
||||
JsonObject JsonObject::fromJsonSafe(const char *json, int len) noexcept(false)
|
||||
{
|
||||
if (len < 0)
|
||||
len = strlen(json);
|
||||
QJsonParseError error;
|
||||
const auto doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
throw JsonParseError(error);
|
||||
|
||||
if (!doc.isObject())
|
||||
throw JsonTypeError("JSON root is not an object");
|
||||
|
||||
return JsonObject(doc.object());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class JsonArray
|
||||
{
|
||||
public:
|
||||
JsonArray() = default;
|
||||
explicit JsonArray(const QJsonArray &json)
|
||||
: m_json(json)
|
||||
, m_jsonIsSet(true)
|
||||
{}
|
||||
|
||||
explicit JsonArray(QJsonArray &&json)
|
||||
: m_json(std::move(json))
|
||||
, m_jsonIsSet(true)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static inline JsonArray fromJson(const char *json, int len = -1);
|
||||
static inline JsonArray fromJson(const QByteArray &json) { return fromJson(json.data(), json.size()); }
|
||||
static inline JsonArray fromJson(const std::string &json) { return fromJson(json.c_str(), json.size()); }
|
||||
|
||||
static inline JsonArray fromJsonSafe(const char *json, int len = -1) noexcept(false);
|
||||
static inline JsonArray fromJsonSafe(const QByteArray &json) noexcept(false) { return fromJson(json.data(), json.size()); }
|
||||
static inline JsonArray fromJsonSafe(const std::string &json) noexcept(false) { return fromJson(json.c_str(), json.size()); }
|
||||
|
||||
|
||||
int size() const { return m_json.size(); }
|
||||
bool isEmpty() const { return m_json.isEmpty(); }
|
||||
bool isValid() const { return m_jsonIsSet; }
|
||||
|
||||
|
||||
template<class T>
|
||||
bool contains(int index) const noexcept(false);
|
||||
|
||||
|
||||
bool isNull(int index) const noexcept(false) { return contains<void>(index); }
|
||||
|
||||
|
||||
template<class T>
|
||||
T at(int index) const noexcept(false);
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
bool isVector() const;
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
QVector<T> toVector() const noexcept(false);
|
||||
|
||||
template<class T>
|
||||
std::vector<T> toStdVector() const;
|
||||
|
||||
bool operator==(const JsonArray &other) const { return m_json == other.m_json; }
|
||||
bool operator!=(const JsonArray &other) const { return !(*this == other); }
|
||||
|
||||
const QJsonArray &operator()() const { return m_json; }
|
||||
private:
|
||||
QJsonArray m_json;
|
||||
bool m_jsonIsSet = false;
|
||||
};
|
||||
|
||||
JsonArray JsonArray::fromJson(const char *json, int len)
|
||||
{
|
||||
if (len < 0)
|
||||
len = strlen(json);
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
return JsonArray();
|
||||
|
||||
if (!doc.isArray())
|
||||
return JsonArray();
|
||||
|
||||
return JsonArray(doc.array());
|
||||
}
|
||||
|
||||
JsonArray JsonArray::fromJsonSafe(const char *json, int len) noexcept(false)
|
||||
{
|
||||
if (len < 0)
|
||||
len = strlen(json);
|
||||
QJsonParseError error;
|
||||
const auto doc = QJsonDocument::fromJson(QByteArray::fromRawData(json, len), &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
throw JsonParseError(error);
|
||||
|
||||
if (!doc.isArray())
|
||||
throw JsonTypeError("JSON root is not an array");
|
||||
|
||||
return JsonArray(doc.array());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
struct JsonValue;
|
||||
|
||||
|
||||
|
||||
template<class Int>
|
||||
struct JsonLimits
|
||||
{
|
||||
static constexpr Int lowest() { return std::numeric_limits<Int>::lowest(); }
|
||||
static constexpr Int max() { return std::numeric_limits<Int>::max(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonLimits<int64_t>
|
||||
{
|
||||
static constexpr int64_t lowest() { return -(static_cast<int64_t>(1) << 53) + 1; }
|
||||
static constexpr int64_t max() { return (static_cast<int64_t>(1) << 53) - 1; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonLimits<uint64_t>
|
||||
{
|
||||
static constexpr uint64_t lowest() { return 0; }
|
||||
static constexpr uint64_t max() { return JsonLimits<int64_t>::max(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class Int>
|
||||
struct JsonIntValue
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value)
|
||||
{
|
||||
if (!value.isDouble())
|
||||
return false;
|
||||
double val = value.toDouble();
|
||||
|
||||
if (val < JsonLimits<Int>::lowest())
|
||||
return false;
|
||||
if (val > JsonLimits<Int>::max())
|
||||
return false;
|
||||
|
||||
double wholeInt = static_cast<Int>(val);
|
||||
return val == wholeInt;
|
||||
}
|
||||
|
||||
static Int value(const QJsonValue &value) noexcept(false)
|
||||
{
|
||||
if (!matchesType(value))
|
||||
throw JsonTypeError("JSON value not of type Int");
|
||||
return static_cast<Int>(value.toDouble());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<int16_t> : JsonIntValue<int16_t>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct JsonValue<uint16_t> : JsonIntValue<uint16_t>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct JsonValue<int32_t> : JsonIntValue<int32_t>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct JsonValue<uint32_t> : JsonIntValue<uint32_t>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct JsonValue<int64_t> : JsonIntValue<int64_t>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct JsonValue<uint64_t> : JsonIntValue<uint64_t>
|
||||
{};
|
||||
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<double>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isDouble(); }
|
||||
|
||||
static double value(const QJsonValue &value) noexcept(false)
|
||||
{
|
||||
if (!matchesType(value))
|
||||
throw JsonTypeError("JSON value not of type Double");
|
||||
return value.toDouble();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<bool>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isBool(); }
|
||||
|
||||
static bool value(const QJsonValue &value) noexcept(false)
|
||||
{
|
||||
if (!matchesType(value))
|
||||
throw JsonTypeError("JSON value not of type Bool");
|
||||
return value.toBool();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<QString>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isString(); }
|
||||
|
||||
static QString value(const QJsonValue &value) noexcept(false)
|
||||
{
|
||||
if (!matchesType(value))
|
||||
throw JsonTypeError("JSON value not of type String");
|
||||
return value.toString();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<std::string>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isString(); }
|
||||
|
||||
static std::string value(const QJsonValue &value)
|
||||
{
|
||||
return JsonValue<QString>::value(value).toStdString();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<QJsonObject>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isObject(); }
|
||||
|
||||
static QJsonObject value(const QJsonValue &value) noexcept(false)
|
||||
{
|
||||
if (!matchesType(value))
|
||||
throw JsonTypeError("JSON value not of type Object");
|
||||
return value.toObject();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<JsonObject>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isObject(); }
|
||||
static JsonObject value(const QJsonValue &value) { return JsonObject(JsonValue<QJsonObject>::value(value)); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<void>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isNull(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<QJsonArray>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isArray(); }
|
||||
|
||||
static QJsonArray value(const QJsonValue &value) noexcept(false)
|
||||
{
|
||||
if (!matchesType(value))
|
||||
throw JsonTypeError("JSON value not of type Array");
|
||||
return value.toArray();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
struct JsonValue<JsonArray>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value) { return value.isArray(); }
|
||||
static JsonArray value(const QJsonValue &value) { return JsonArray(JsonValue<QJsonArray>::value(value)); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
struct JsonValue<QVector<T>>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value)
|
||||
{
|
||||
if (!value.isArray())
|
||||
return false;
|
||||
|
||||
const JsonArray array(value.toArray());
|
||||
return array.isVector<T>();
|
||||
}
|
||||
|
||||
static QVector<T> value(const QJsonValue &value)
|
||||
{
|
||||
const JsonArray array(JsonValue<QJsonArray>::value(value));
|
||||
return array.toVector<T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
struct JsonValue<std::vector<T>>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value)
|
||||
{
|
||||
return JsonValue<QVector<T>>::matchesType(value);
|
||||
}
|
||||
|
||||
static std::vector<T> value(const QJsonValue &value)
|
||||
{
|
||||
const JsonArray array(JsonValue<QJsonArray>::value(value));
|
||||
return array.toStdVector<T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
struct JsonValue<QMap<QString, T>>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value)
|
||||
{
|
||||
if (!value.isObject())
|
||||
return false;
|
||||
|
||||
const JsonObject obj(JsonValue<QJsonObject>::value(value));
|
||||
return obj.isMap<T>();
|
||||
}
|
||||
|
||||
static QMap<QString, T> value(const QJsonValue &value)
|
||||
{
|
||||
const JsonObject obj(JsonValue<QJsonObject>::value(value));
|
||||
return obj.toMap<T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
struct JsonValue<std::map<std::string, T>>
|
||||
{
|
||||
static bool matchesType(const QJsonValue &value)
|
||||
{
|
||||
return JsonValue<QMap<QString, T>>::matchesType(value);
|
||||
}
|
||||
|
||||
static std::map<std::string, T> value(const QJsonValue &value)
|
||||
{
|
||||
const JsonObject obj(JsonValue<QJsonObject>::value(value));
|
||||
return obj.toStdMap<T>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
bool JsonObject::contains(const Key &key) const
|
||||
{
|
||||
if (!m_json.contains(key))
|
||||
return false;
|
||||
return JsonValue<T>::matchesType(m_json.value(key));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T JsonObject::value(const Key &key) const noexcept(false)
|
||||
{
|
||||
if (!containsAny(key))
|
||||
throw JsonKeyError("no element with key: " + static_cast<const QString &>(key).toStdString());
|
||||
try
|
||||
{
|
||||
return JsonValue<T>::value(m_json.value(key));
|
||||
}
|
||||
catch(const JsonTypeError &typeError)
|
||||
{
|
||||
throw JsonTypeError(std::string(typeError.what()) + " - key: " + static_cast<const QString &>(key).toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T JsonObject::valueOr(const Key &key, const T &defaultValue) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return value<T>(key);
|
||||
}
|
||||
|
||||
catch (const JsonKeyError &)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool JsonObject::isMap() const
|
||||
{
|
||||
const QStringList keys = m_json.keys();
|
||||
for (const auto &key : keys)
|
||||
if (!JsonValue<T>::matchesType(m_json.value(key)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
QMap<QString, T> JsonObject::toMap() const
|
||||
{
|
||||
QMap<QString, T> result;
|
||||
for (auto current = m_json.begin(); current != m_json.end(); ++current)
|
||||
result.insert(current.key(), JsonValue<T>::value(current.value()));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::map<std::string, T> JsonObject::toStdMap() const
|
||||
{
|
||||
std::map<std::string, T> result;
|
||||
for (auto current = m_json.begin(); current != m_json.end(); ++current)
|
||||
result.insert({current.key().toStdString(), JsonValue<T>::value(current.value())});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class T>
|
||||
bool JsonArray::contains(int index) const noexcept(false)
|
||||
{
|
||||
if (index < 0 || index >= m_json.size())
|
||||
throw JsonRangeError("index out of range: " + std::to_string(index));
|
||||
return JsonValue<T>::matchesType(m_json.at(index));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T JsonArray::at(int index) const noexcept(false)
|
||||
{
|
||||
if (index < 0 || index >= m_json.size())
|
||||
throw JsonRangeError("index out of range: " + std::to_string(index));
|
||||
return JsonValue<T>::value(m_json.at(index));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool JsonArray::isVector() const
|
||||
{
|
||||
for (const QJsonValue &value : m_json)
|
||||
if (!JsonValue<T>::matchesType(value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
QVector<T> JsonArray::toVector() const
|
||||
{
|
||||
QVector<T> result;
|
||||
result.reserve(m_json.size());
|
||||
for (const QJsonValue &value : m_json)
|
||||
result.push_back(JsonValue<T>::value(value));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::vector<T> JsonArray::toStdVector() const
|
||||
{
|
||||
std::vector<T> result;
|
||||
result.reserve(m_json.size());
|
||||
for (const QJsonValue &value : m_json)
|
||||
result.push_back(JsonValue<T>::value(value));
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#include "lts_portrange.h"
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
LtsUtils::LtsPortRange fromQPoint(const QPoint &point)
|
||||
{
|
||||
if (point.x() < 1025 || point.y() > 65535 || point.x() > point.y())
|
||||
return {};
|
||||
|
||||
return {static_cast<quint16>(point.x()), static_cast<quint16>(point.y())};
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
|
||||
#include <QPair>
|
||||
#include <QPoint>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
typedef QPair<quint16, quint16> LtsPortRange;
|
||||
|
||||
LTSUTILSCORE_EXPORT LtsPortRange fromQPoint(const QPoint &point);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
|
||||
#include "lts_translatable_identifier.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
LtsTranslatableIdentifier::LtsTranslatableIdentifier(const QString &identifier, const QObject *context,
|
||||
const char *translatableText)
|
||||
: m_identifier(identifier)
|
||||
, m_translationContext(context->metaObject()->className())
|
||||
, m_translatableText(translatableText)
|
||||
{}
|
||||
|
||||
|
||||
const QString LtsTranslatableIdentifier::translatedText(const char *disambiguation, int n) const
|
||||
{
|
||||
if (m_identifier.isEmpty() || m_translationContext == nullptr || m_translatableText == nullptr)
|
||||
return {};
|
||||
return QCoreApplication::translate(m_translationContext, m_translatableText, disambiguation, n);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
class LTSUTILSCORE_EXPORT LtsTranslatableIdentifier
|
||||
{
|
||||
public:
|
||||
LtsTranslatableIdentifier()
|
||||
: m_translationContext(nullptr)
|
||||
, m_translatableText("")
|
||||
{}
|
||||
|
||||
LtsTranslatableIdentifier(const QString &identifier, const QObject *context, const char *translatableText);
|
||||
const QString &identifier() const { return m_identifier; }
|
||||
const QString translatedText(const char *disambiguation = nullptr, int n = -1) const;
|
||||
const QString _context() const { return QString::fromUtf8(m_translationContext); }
|
||||
|
||||
private:
|
||||
QString m_identifier;
|
||||
const char *m_translationContext;
|
||||
|
||||
const char *m_translatableText;
|
||||
};
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <qglobal.h>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
inline bool isPositionTimeOutdated(qint64 curTime, qint64 posTime)
|
||||
{
|
||||
|
||||
return curTime > posTime + 11 * 60 * 1000;
|
||||
}
|
||||
}
|
||||
Executable
+169
@@ -0,0 +1,169 @@
|
||||
|
||||
|
||||
|
||||
#include "pgarray.h"
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
|
||||
#include <functional>
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
static inline QString convertToPgString(const std::vector<T> &data)
|
||||
{
|
||||
if (data.size() == 0)
|
||||
return QLatin1String("{}");
|
||||
QString ret(QLatin1Char('{'));
|
||||
for (T i : data)
|
||||
ret += QString::number(i) + QLatin1Char(',');
|
||||
ret.chop(1);
|
||||
ret += QLatin1Char('}');
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT QString IntArray::toPgString() const
|
||||
{
|
||||
return convertToPgString(m_data);
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT QString BigintArray::toPgString() const
|
||||
{
|
||||
return convertToPgString(m_data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T convertFromPgString(const QString &str, const std::function<typename T::Type(const QStringRef &val, bool *bOk)> &convertFunc)
|
||||
{
|
||||
typename T::ContainerType result;
|
||||
|
||||
int idxStart = str.indexOf(QLatin1Char('{'));
|
||||
int idxEnd = str.lastIndexOf(QLatin1Char('}'));
|
||||
if (idxEnd < idxStart)
|
||||
return T(result);
|
||||
if (idxStart < 0 && idxEnd < 0)
|
||||
{
|
||||
|
||||
idxStart = -1;
|
||||
idxEnd = str.length() + 1;
|
||||
}
|
||||
|
||||
const QStringRef strRef = str.midRef(idxStart + 1, idxEnd - idxStart - 1);
|
||||
const QVector<QStringRef> vec = strRef.split(QLatin1Char(','), QString::SkipEmptyParts);
|
||||
result.reserve(vec.size());
|
||||
for (const QStringRef &s : vec)
|
||||
{
|
||||
bool ok = false;
|
||||
const typename T::Type i = convertFunc(s, &ok);
|
||||
if (ok)
|
||||
result.push_back(i);
|
||||
}
|
||||
|
||||
return T(result);
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT IntArray IntArray::fromPgString(const QString &str)
|
||||
{
|
||||
return convertFromPgString<IntArray>(str,
|
||||
[](const QStringRef & val, bool * bOk) { return val.toInt(bOk);});
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT BigintArray BigintArray::fromPgString(const QString &str)
|
||||
{
|
||||
return convertFromPgString<BigintArray>(str,
|
||||
[](const QStringRef & val, bool * bOk) { return val.toLongLong(bOk);});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
static inline QString convertToPgString(const std::map<T, T> &data)
|
||||
{
|
||||
if (data.size() == 0)
|
||||
return QLatin1String("{}");
|
||||
QString ret(QLatin1Char('{'));
|
||||
for (const auto &it : data)
|
||||
{
|
||||
ret += QLatin1Char('{') + QString::number(it.first) +
|
||||
QLatin1Char(',') + QString::number(it.second) + QLatin1String("},");
|
||||
}
|
||||
ret.chop(1);
|
||||
ret += QLatin1Char('}');
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT QString IntArray2D::toPgString() const
|
||||
{
|
||||
return convertToPgString<IntArray2D::Type>(m_data);
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT QString BigintArray2D::toPgString() const
|
||||
{
|
||||
return convertToPgString<BigintArray2D::Type>(m_data);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
static inline T convertFromPgString2D(const QString &str, const std::function<typename T::Type(const QStringRef &val)> &convertFunc)
|
||||
{
|
||||
typename T::ContainerType ret;
|
||||
int idxStart = str.indexOf(QLatin1Char('{'));
|
||||
int idxEnd = str.lastIndexOf(QLatin1Char('}'));
|
||||
if (idxEnd < idxStart)
|
||||
return T(ret);
|
||||
if (idxStart < 0 && idxEnd < 0)
|
||||
{
|
||||
|
||||
idxStart = -1;
|
||||
idxEnd = str.length() + 1;
|
||||
}
|
||||
|
||||
const QStringRef strRef = str.midRef(idxStart + 1, idxEnd - idxStart - 1);
|
||||
idxStart = strRef.indexOf(QLatin1Char('{'));
|
||||
while (idxStart >= 0)
|
||||
{
|
||||
idxEnd = strRef.indexOf(QLatin1Char('}'), idxStart);
|
||||
if (idxEnd < 0)
|
||||
break;
|
||||
|
||||
const QStringRef keyValueStr = strRef.mid(idxStart + 1, idxEnd - idxStart - 1);
|
||||
const QVector<QStringRef> keyValuePair = keyValueStr.split(QLatin1Char(','), QString::SkipEmptyParts);
|
||||
if (keyValuePair.size() != 2)
|
||||
break;
|
||||
const typename T::Type key = convertFunc(keyValuePair.at(0).trimmed());
|
||||
const typename T::Type value = convertFunc(keyValuePair.at(1).trimmed());
|
||||
ret.insert(std::make_pair(key, value));
|
||||
|
||||
|
||||
idxStart = strRef.indexOf(QLatin1Char('{'), idxEnd + 1);
|
||||
}
|
||||
|
||||
return T(ret);
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT IntArray2D IntArray2D::fromPgString(const QString &str)
|
||||
{
|
||||
return convertFromPgString2D<IntArray2D>(str,
|
||||
[](const QStringRef & val) { return val.toInt();});
|
||||
}
|
||||
|
||||
template<>
|
||||
LTSUTILSCORE_EXPORT BigintArray2D BigintArray2D::fromPgString(const QString &str)
|
||||
{
|
||||
return convertFromPgString2D<BigintArray2D>(str,
|
||||
[](const QStringRef & val) { return val.toLongLong();});
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
template<typename T, typename C>
|
||||
class PgArray
|
||||
{
|
||||
public:
|
||||
using Type = T;
|
||||
using ContainerType = C;
|
||||
|
||||
PgArray() = default;
|
||||
Q_DECL_CONSTEXPR explicit PgArray(const C &data)
|
||||
: m_data(data)
|
||||
{}
|
||||
|
||||
Q_DECL_CONSTEXPR C operator()()const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
QString toPgString() const;
|
||||
static PgArray fromPgString(const QString &str);
|
||||
private:
|
||||
C m_data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
using IntArray = PgArray<int, std::vector<int>>;
|
||||
using BigintArray = PgArray<int64_t, std::vector<int64_t>>;
|
||||
|
||||
|
||||
using IntArray2D = PgArray<int, std::map<int, int>>;
|
||||
using BigintArray2D = PgArray<int64_t, std::map<int64_t, int64_t>>;
|
||||
|
||||
}
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
|
||||
|
||||
|
||||
#include "promise.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
Promise::Promise(const std::function<void(ResultFunction resolve, ResultFunction reject)> &function, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_functionAlreadySet(false)
|
||||
, m_timeout(new QTimer(this))
|
||||
, m_resultSet(false)
|
||||
, m_timeoutSet(false)
|
||||
{
|
||||
m_timeout->setSingleShot(true);
|
||||
connect(m_timeout, &QTimer::timeout,
|
||||
this, &Promise::slotTimeout);
|
||||
setFunction(function);
|
||||
}
|
||||
|
||||
|
||||
void Promise::setFunction(const std::function<void(ResultFunction resolve, ResultFunction reject)> &function)
|
||||
{
|
||||
if (!function || m_functionAlreadySet)
|
||||
return;
|
||||
|
||||
m_functionAlreadySet = true;
|
||||
|
||||
if (m_timeoutSet)
|
||||
m_timeout->start();
|
||||
|
||||
|
||||
QTimer::singleShot(0, this, [this, function]()
|
||||
{
|
||||
|
||||
function([this](const QVariant &value)
|
||||
{
|
||||
resolve(value);
|
||||
}, [this](const QVariant &value)
|
||||
{
|
||||
|
||||
reject(value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Promise::setTimeout(int interval)
|
||||
{
|
||||
m_timeout->stop();
|
||||
m_timeout->setInterval(interval);
|
||||
m_timeoutSet = (interval != 0);
|
||||
|
||||
if (interval != 0 && m_functionAlreadySet)
|
||||
m_timeout->start();
|
||||
}
|
||||
|
||||
|
||||
void Promise::slotTimeout()
|
||||
{
|
||||
reject(QStringLiteral("timeout"));
|
||||
}
|
||||
|
||||
|
||||
void Promise::resolve(const QVariant &value)
|
||||
{
|
||||
if (m_resultSet)
|
||||
return;
|
||||
m_resultSet = true;
|
||||
|
||||
m_timeout->stop();
|
||||
m_resolveFn(value);
|
||||
}
|
||||
|
||||
|
||||
void Promise::reject(const QVariant &value)
|
||||
{
|
||||
if (m_resultSet)
|
||||
return;
|
||||
m_resultSet = true;
|
||||
|
||||
m_timeout->stop();
|
||||
m_rejectFn(value);
|
||||
}
|
||||
|
||||
|
||||
Promise &Promise::resolved(ResultFunction resolveFn)
|
||||
{
|
||||
m_resolveFn = resolveFn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Promise &Promise::rejected(ResultFunction rejectFn)
|
||||
{
|
||||
m_rejectFn = rejectFn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
class QTimer;
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
typedef std::function<void(const QVariant &value)> RawResultFunction;
|
||||
typedef const RawResultFunction &ResultFunction;
|
||||
|
||||
|
||||
|
||||
|
||||
class LTSUTILSCORE_EXPORT Promise : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
|
||||
Promise(const std::function<void(ResultFunction resolve, ResultFunction reject)> &function, QObject *parent = nullptr);
|
||||
|
||||
|
||||
|
||||
void setFunction(const std::function<void(ResultFunction resolve, ResultFunction reject)> &function);
|
||||
|
||||
|
||||
|
||||
void resolve(const QVariant &value = QVariant());
|
||||
|
||||
|
||||
|
||||
void reject(const QVariant &value = QVariant());
|
||||
|
||||
|
||||
|
||||
void setTimeout(int interval);
|
||||
|
||||
|
||||
|
||||
Promise &resolved(ResultFunction resolveFn);
|
||||
|
||||
|
||||
|
||||
Promise &rejected(ResultFunction rejectFn);
|
||||
|
||||
|
||||
private:
|
||||
RawResultFunction m_resolveFn;
|
||||
RawResultFunction m_rejectFn;
|
||||
bool m_functionAlreadySet;
|
||||
QTimer *m_timeout;
|
||||
bool m_resultSet;
|
||||
bool m_timeoutSet;
|
||||
|
||||
|
||||
private slots:
|
||||
void slotTimeout();
|
||||
};
|
||||
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
#include "promise_examples.h"
|
||||
#include "promise.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QTest>
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
|
||||
void PromiseExample1_Class::doSomethingAsync1()
|
||||
{
|
||||
QTimer::singleShot(200, this, [this]()
|
||||
{
|
||||
emit signalDone1(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void PromiseExample1_Class::doSomethingAsync2()
|
||||
{
|
||||
QTimer::singleShot(450, this, [this]()
|
||||
{
|
||||
emit signalDone2(true, 1234);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
PromiseExample1::PromiseExample1(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_success(false)
|
||||
{
|
||||
|
||||
|
||||
|
||||
auto func = [this](ResultFunction resolve
|
||||
, ResultFunction reject )
|
||||
{
|
||||
|
||||
PromiseExample1_Class *c = new PromiseExample1_Class(this);
|
||||
|
||||
|
||||
connect(c, &PromiseExample1_Class::signalDone1,
|
||||
this, [this, c, resolve, reject](bool didThisWork)
|
||||
{
|
||||
|
||||
if (!didThisWork)
|
||||
{
|
||||
|
||||
reject(QStringLiteral("First Action did not succeed"));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
|
||||
connect(c, &PromiseExample1_Class::signalDone2,
|
||||
this, [resolve, reject](bool didThisWork, int someNumberThatReturns)
|
||||
{
|
||||
if (didThisWork)
|
||||
resolve(someNumberThatReturns);
|
||||
else
|
||||
reject(QStringLiteral("Second Action did not succeed"));
|
||||
|
||||
|
||||
});
|
||||
|
||||
c->doSomethingAsync2();
|
||||
}
|
||||
});
|
||||
|
||||
c->doSomethingAsync1();
|
||||
};
|
||||
Promise *p = new Promise(func, this);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
p->resolved([this](const QVariant &value)
|
||||
{
|
||||
|
||||
qDebug() << "example1 promise succeeded! 'someNumberThatReturns' was" << value;
|
||||
|
||||
m_success = true;
|
||||
|
||||
}).rejected([](const QVariant &value)
|
||||
{
|
||||
|
||||
qDebug() << "example1 promise failed.." << value;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PromiseExample2::PromiseExample2(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_success(false)
|
||||
{
|
||||
|
||||
|
||||
|
||||
Promise *p = new Promise([](ResultFunction , ResultFunction )
|
||||
{
|
||||
|
||||
}, this);
|
||||
|
||||
p->setTimeout(1000);
|
||||
|
||||
p->resolved([](const QVariant &)
|
||||
{
|
||||
QFAIL("This test case's promise should never resolve");
|
||||
});
|
||||
|
||||
p->rejected([this](const QVariant &val)
|
||||
{
|
||||
QVERIFY2(val == QStringLiteral("timeout"), "Promise didn't reject with string \"timeout\"");
|
||||
qDebug() << "Promise rejected as expected";
|
||||
m_success = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PromiseExample3::PromiseExample3(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_success(true)
|
||||
, m_alreadyRejected(false)
|
||||
{
|
||||
|
||||
|
||||
|
||||
Promise *p = new Promise([](ResultFunction , ResultFunction reject)
|
||||
{
|
||||
reject(QString());
|
||||
}, this);
|
||||
|
||||
p->setTimeout(1000);
|
||||
|
||||
p->resolved([](const QVariant &)
|
||||
{
|
||||
QFAIL("This test case's promise should never resolve");
|
||||
});
|
||||
|
||||
p->rejected([this](const QVariant &val)
|
||||
{
|
||||
qDebug() << "rejected with val" << val;
|
||||
QVERIFY2(val == QString(), "Promise didn't reject with an expected empty string.");
|
||||
QVERIFY2(!m_alreadyRejected, "Promise rejected more than once");
|
||||
|
||||
m_alreadyRejected = true;
|
||||
qDebug() << "Promise rejected as expected";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <QObject>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
|
||||
class PromiseExample1_Class : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
void doSomethingAsync1();
|
||||
void doSomethingAsync2();
|
||||
|
||||
signals:
|
||||
void signalDone1(bool didThisWork);
|
||||
void signalDone2(bool didThisWork, int someNumberThatReturns);
|
||||
};
|
||||
|
||||
|
||||
class PromiseExample1 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PromiseExample1(QObject *parent = nullptr);
|
||||
|
||||
bool isSuccess() const { return m_success; }
|
||||
|
||||
private:
|
||||
bool m_success;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class PromiseExample2 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PromiseExample2(QObject *parent = nullptr);
|
||||
|
||||
bool isSuccess() const { return m_success; }
|
||||
|
||||
private:
|
||||
bool m_success;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class PromiseExample3 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PromiseExample3(QObject *parent = nullptr);
|
||||
|
||||
bool isSuccess() const { return m_success; }
|
||||
|
||||
private:
|
||||
bool m_success;
|
||||
bool m_alreadyRejected;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <limits>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
void inline set8Byte(const T in, QByteArray& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
out += (in & 0xFF00000000000000UL) >> 56;
|
||||
out += (in & 0x00FF000000000000UL) >> 48;
|
||||
out += (in & 0x0000FF0000000000UL) >> 40;
|
||||
out += (in & 0x000000FF00000000UL) >> 32;
|
||||
out += (in & 0x00000000FF000000UL) >> 24;
|
||||
out += (in & 0x0000000000FF0000UL) >> 16;
|
||||
out += (in & 0x000000000000FF00UL) >> 8;
|
||||
out += (in & 0x00000000000000FFUL);
|
||||
}
|
||||
|
||||
template<>
|
||||
void inline set8Byte(const double in, QByteArray &out)
|
||||
{
|
||||
union hack
|
||||
{
|
||||
double d;
|
||||
int64_t i;
|
||||
} h;
|
||||
h.d = in;
|
||||
set8Byte(h.i, out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get8Byte(const QByteArray& baData,
|
||||
size_t &pos,
|
||||
T& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
static_assert(sizeof(T) >= 8, "sizeof(out) is smaller than 8 bytes - will not fit");
|
||||
out = T();
|
||||
|
||||
if(size_t(baData.size()) < pos + 8)
|
||||
return false;
|
||||
|
||||
const char *data = baData.data() + pos;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
out |= static_cast<signed long long>(data[0]) << 56;
|
||||
else
|
||||
out |= (data[0] & 0xFFULL) << 56;
|
||||
out |= (data[1] & 0xFFULL) << 48;
|
||||
out |= (data[2] & 0xFFULL) << 40;
|
||||
out |= (data[3] & 0xFFULL) << 32;
|
||||
out |= (data[4] & 0xFFULL) << 24;
|
||||
out |= (data[5] & 0xFFULL) << 16;
|
||||
out |= (data[6] & 0xFFULL) << 8;
|
||||
out |= (data[7] & 0xFFULL);
|
||||
|
||||
pos += 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool inline get8Byte(const QByteArray& baData,
|
||||
size_t &pos,
|
||||
double& out)
|
||||
{
|
||||
out = 0.;
|
||||
union hack
|
||||
{
|
||||
double d;
|
||||
int64_t i;
|
||||
} h;
|
||||
if (!get8Byte(baData, pos, h.i))
|
||||
return false;
|
||||
out = h.d;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get8Byte(const QByteArray& data, T& out)
|
||||
{
|
||||
size_t pos = 0;
|
||||
return get8Byte(data, pos, out);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void inline set4Byte(const T in, QByteArray& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
out += (in & 0xFF000000) >> 24;
|
||||
out += (in & 0x00FF0000) >> 16;
|
||||
out += (in & 0x0000FF00) >> 8;
|
||||
out += (in & 0x000000FF);
|
||||
}
|
||||
|
||||
template<>
|
||||
void inline set4Byte(const double in, QByteArray& out)
|
||||
{
|
||||
int tmp = qRound(in);
|
||||
return set4Byte(tmp, out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get4Byte(const QByteArray& baData,
|
||||
size_t &pos,
|
||||
T& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
static_assert(sizeof(T) >= 4, "sizeof(out) is smaller than 4 bytes - will not fit");
|
||||
out = T();
|
||||
|
||||
if(size_t(baData.size()) < pos + 4)
|
||||
return false;
|
||||
|
||||
const char *data = baData.data() + pos;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
out |= data[0] << 24;
|
||||
else
|
||||
out |= (data[0] & 0xFFU) << 24;
|
||||
out |= (data[1] & 0xFFU) << 16;
|
||||
out |= (data[2] & 0xFFU) << 8;
|
||||
out |= (data[3] & 0xFFU);
|
||||
|
||||
pos += 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool inline get4Byte(const QByteArray& baData,
|
||||
size_t &pos,
|
||||
double& out)
|
||||
{
|
||||
out = 0.;
|
||||
int tmp;
|
||||
if(!get4Byte(baData, pos, tmp))
|
||||
return false;
|
||||
out = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get4Byte(const QByteArray& data, T& out)
|
||||
{
|
||||
size_t pos = 0;
|
||||
return get4Byte(data, pos, out);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void inline set3Byte(const T in, QByteArray& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
out += (in & 0x00FF0000) >> 16;
|
||||
out += (in & 0x0000FF00) >> 8;
|
||||
out += (in & 0x000000FF);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get3Byte(const QByteArray& baData,
|
||||
size_t &pos,
|
||||
T& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
static_assert(sizeof(T) >= 3, "sizeof(out) is smaller than 3 bytes - will not fit");
|
||||
out = T();
|
||||
|
||||
if(size_t(baData.size()) < pos + 3)
|
||||
return false;
|
||||
|
||||
const char *data = baData.data() + pos;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
out |= data[0] << 16;
|
||||
else
|
||||
out |= (data[0] & 0xFFU) << 16;
|
||||
out |= (data[1] & 0xFFU) << 8;
|
||||
out |= (data[2] & 0xFFU);
|
||||
|
||||
pos += 3;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get3Byte(const QByteArray& data, T& out)
|
||||
{
|
||||
size_t pos = 0;
|
||||
return get3Byte(data, pos, out);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void inline set2Byte(const T in, QByteArray& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
out += (in & 0x0000FF00) >> 8;
|
||||
out += (in & 0x000000FF);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get2Byte(const QByteArray& baData,
|
||||
size_t &pos,
|
||||
T& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
static_assert(sizeof(T) >= 2, "sizeof(out) is smaller than 2 bytes - will not fit");
|
||||
out = T();
|
||||
|
||||
if(size_t(baData.size()) < pos + 2)
|
||||
return false;
|
||||
|
||||
const char *data = baData.data() + pos;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
out |= data[0] << 8;
|
||||
else
|
||||
out |= (data[0] & 0xFFU) << 8;
|
||||
out |= data[1] & 0xFFU;
|
||||
|
||||
pos += 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get2Byte(const QByteArray& data, T& out)
|
||||
{
|
||||
size_t pos = 0;
|
||||
return get2Byte(data, pos, out);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void inline set1Byte(const T in, QByteArray& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
unsigned char c = in;
|
||||
out += c;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get1Byte(const QByteArray& baData,
|
||||
size_t &pos,
|
||||
T& out)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "Only integrals allowed for raw byte accessd");
|
||||
out = T();
|
||||
|
||||
if(size_t(baData.size()) < pos + 1)
|
||||
return false;
|
||||
|
||||
const char *data = baData.data() + pos;
|
||||
|
||||
if(std::numeric_limits<T>::is_signed)
|
||||
out = (T)(data[0]);
|
||||
else
|
||||
out = (T)(data[0] & 0xFF);
|
||||
|
||||
pos += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool inline get1Byte(const QByteArray& data, T& out)
|
||||
{
|
||||
size_t pos = 0;
|
||||
return get1Byte(data, pos, out);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
#include "qplane3d.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QPlane3D::QPlane3D(const QPoint3D &pos, const QPoint3D &vec1, const QPoint3D &vec2)
|
||||
: m_pos(pos)
|
||||
, m_vec1(vec1)
|
||||
, m_vec2(vec2)
|
||||
{
|
||||
m_normal = QPoint3D::crossProduct(m_vec1, m_vec2);
|
||||
m_planeC = QPoint3D::dotProduct(m_pos, m_normal);
|
||||
}
|
||||
|
||||
|
||||
QPoint3D QPlane3D::intersection(const QPoint3D &point, const QPoint3D &direction) const
|
||||
{
|
||||
const double dotProduct = QPoint3D::dotProduct(direction, m_normal);
|
||||
if (qFuzzyIsNull(dotProduct))
|
||||
return inPlane(point) ? point : QPoint3D();
|
||||
const double distance = distanceTo(point) / std::abs(dotProduct);
|
||||
return point + distance * direction;
|
||||
}
|
||||
|
||||
bool QPlane3D::intersects(const QPoint3D &direction) const
|
||||
{
|
||||
return !isParallel(direction);
|
||||
}
|
||||
|
||||
bool QPlane3D::isParallel(const QPoint3D &direction) const
|
||||
{
|
||||
return qFuzzyIsNull(QPoint3D::dotProduct(m_normal, direction));
|
||||
}
|
||||
|
||||
bool QPlane3D::inPlane(const QPoint3D &point) const
|
||||
{
|
||||
if (point == m_pos)
|
||||
return true;
|
||||
return qFuzzyIsNull(QPoint3D::dotProduct(m_normal, point - m_pos));
|
||||
}
|
||||
|
||||
double QPlane3D::distanceTo(const QPoint3D &point) const
|
||||
{
|
||||
return QPoint3D::dotProduct(point - m_pos, m_normal);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QPolygon3D::QPolygon3D(const QVector<QPoint3D> &vertices)
|
||||
: QPlane3D(vertices[0], vertices[1], vertices[2])
|
||||
, m_vertices(vertices)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool QPolygon3D::containsPoint(const QPoint3D &point) const
|
||||
{
|
||||
if (!inPlane(point))
|
||||
return false;
|
||||
|
||||
if (contains(point))
|
||||
return true;
|
||||
|
||||
double angleSum = 0.;
|
||||
double cosTheta = 0.;
|
||||
const int numberOfVertices = m_vertices.size();
|
||||
for (int i = 0; i < m_vertices.size(); ++i)
|
||||
{
|
||||
const QPoint3D &first = m_vertices[i % numberOfVertices] - point;
|
||||
const QPoint3D &second = m_vertices[(i + 1) % numberOfVertices] - point;
|
||||
|
||||
cosTheta = QPoint3D::dotProduct(first, second) / (first.length() * second.length());
|
||||
angleSum += acos(cosTheta);
|
||||
}
|
||||
return qFuzzyIsNull(M_2_PI - angleSum);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qpoint3d.h"
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
|
||||
#include <QVector>
|
||||
#include <QPointF>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
class LTSUTILSCORE_EXPORT QPlane3D
|
||||
{
|
||||
|
||||
public:
|
||||
QPlane3D(const QPoint3D &pos, const QPoint3D &vec1, const QPoint3D &vec2);
|
||||
|
||||
QPoint3D intersection(const QPoint3D &point, const QPoint3D &direction) const;
|
||||
bool intersects(const QPoint3D &direction) const;
|
||||
bool isParallel(const QPoint3D &direction) const;
|
||||
bool inPlane(const QPoint3D &point) const;
|
||||
double distanceTo(const QPoint3D &point) const;
|
||||
|
||||
const QPoint3D &pos() const { return m_pos; }
|
||||
const QPoint3D &vec1() const { return m_vec1; }
|
||||
const QPoint3D &vec2() const { return m_vec2; }
|
||||
const QPoint3D &normal() const { return m_normal; }
|
||||
double planeC() const { return m_planeC; }
|
||||
|
||||
protected:
|
||||
QPoint3D m_pos;
|
||||
QPoint3D m_vec1;
|
||||
QPoint3D m_vec2;
|
||||
QPoint3D m_normal;
|
||||
double m_planeC;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class LTSUTILSCORE_EXPORT QPolygon3D : public QPlane3D
|
||||
{
|
||||
|
||||
public:
|
||||
QPolygon3D(const QVector<QPoint3D> &vertices);
|
||||
|
||||
bool contains(const QPoint3D &point) const { return m_vertices.contains(point); }
|
||||
bool containsPoint(const QPoint3D &point) const;
|
||||
|
||||
private:
|
||||
QVector<QPoint3D> m_vertices;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
|
||||
|
||||
|
||||
#include "qpoint3d.h"
|
||||
#include <QDataStream>
|
||||
|
||||
using namespace LtsUtils;
|
||||
|
||||
|
||||
double QPoint3D::distanceToLine(const QPoint3D &point, const QPoint3D &direction) const
|
||||
{
|
||||
if (direction.isNull())
|
||||
return (*this - point).length();
|
||||
QPoint3D p = point + dotProduct(*this - point, direction) * direction;
|
||||
return (*this - p).length();
|
||||
}
|
||||
|
||||
double QPoint3D::distanceToPlane(const QPoint3D &plane, const QPoint3D &normal) const
|
||||
{
|
||||
return dotProduct(*this - plane, normal);
|
||||
}
|
||||
|
||||
double QPoint3D::distanceToPlane(const QPoint3D &plane1, const QPoint3D &plane2, const QPoint3D &plane3) const
|
||||
{
|
||||
QPoint3D n = normal(plane2 - plane1, plane3 - plane1);
|
||||
return dotProduct(*this - plane1, n);
|
||||
}
|
||||
|
||||
double QPoint3D::distanceToPoint(const QPoint3D &point) const
|
||||
{
|
||||
return (*this - point).length();
|
||||
}
|
||||
|
||||
|
||||
QDataStream &operator<<(QDataStream &s, const QPoint3D &p)
|
||||
{
|
||||
s << p.x() << p.y() << p.z();
|
||||
return s;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &s, QPoint3D &p)
|
||||
{
|
||||
double x, y, z;
|
||||
s >> x; p.rx() = x;
|
||||
s >> y; p.ry() = y;
|
||||
s >> z; p.rz() = z;
|
||||
return s;
|
||||
}
|
||||
Executable
+296
@@ -0,0 +1,296 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qpoint.h>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
class QPoint3D
|
||||
{
|
||||
public:
|
||||
enum class Axis { X, Y, Z };
|
||||
|
||||
Q_DECL_CONSTEXPR QPoint3D() = default;
|
||||
Q_DECL_CONSTEXPR QPoint3D(double xpos, double ypos, double zpos);
|
||||
template<typename T>
|
||||
explicit QPoint3D(const T &data)
|
||||
: xp(data.offsetX())
|
||||
, yp(data.offsetY())
|
||||
, zp(data.offsetZ())
|
||||
{}
|
||||
|
||||
inline bool isNull() const;
|
||||
|
||||
Q_DECL_CONSTEXPR inline double x() const;
|
||||
Q_DECL_CONSTEXPR inline double y() const;
|
||||
Q_DECL_CONSTEXPR inline double z() const;
|
||||
Q_DECL_RELAXED_CONSTEXPR inline void setX(double xpos);
|
||||
Q_DECL_RELAXED_CONSTEXPR inline void setY(double ypos);
|
||||
Q_DECL_RELAXED_CONSTEXPR inline void setZ(double zpos);
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline double &rx();
|
||||
Q_DECL_RELAXED_CONSTEXPR inline double &ry();
|
||||
Q_DECL_RELAXED_CONSTEXPR inline double &rz();
|
||||
|
||||
Q_DECL_CONSTEXPR inline QPointF toPointF() const;
|
||||
static QPoint3D fromQPointF(const QPointF &p)
|
||||
{
|
||||
return QPoint3D(p.x(), p.y(), .0);
|
||||
}
|
||||
|
||||
inline double lengthSquared() const { return (xp * xp) + (yp * yp) + (zp * zp); }
|
||||
inline double length() const { return std::sqrt(lengthSquared()); }
|
||||
inline void normalize()
|
||||
{
|
||||
double len = xp * xp + yp * yp + zp * zp;
|
||||
if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len))
|
||||
return;
|
||||
|
||||
len = std::sqrt(len);
|
||||
|
||||
xp /= len;
|
||||
yp /= len;
|
||||
zp /= len;
|
||||
}
|
||||
inline QPoint3D normalized() const
|
||||
{
|
||||
double len = xp * xp + yp * yp + zp * zp;
|
||||
if (qFuzzyIsNull(len - 1.0f))
|
||||
return *this;
|
||||
else if (!qFuzzyIsNull(len))
|
||||
return *this / std::sqrt(len);
|
||||
else
|
||||
return QPoint3D(*this);
|
||||
}
|
||||
|
||||
inline void rotate(const double &phi, const Axis &axis)
|
||||
{
|
||||
const double sin = std::sin(phi);
|
||||
const double cos = std::cos(phi);
|
||||
switch (axis)
|
||||
{
|
||||
case Axis::X:
|
||||
{
|
||||
const double _yp = (cos * yp) - (sin * zp);
|
||||
const double _zp = (sin * yp) + (cos * zp);
|
||||
yp = _yp;
|
||||
zp = _zp;
|
||||
break;
|
||||
}
|
||||
case Axis::Y:
|
||||
{
|
||||
const double _xp = (cos * xp) + (sin * zp);
|
||||
const double _zp = (-sin * xp) + (cos * zp);
|
||||
xp = _xp;
|
||||
zp = _zp;
|
||||
break;
|
||||
}
|
||||
case Axis::Z:
|
||||
{
|
||||
const double _xp = (cos * xp) - (sin * yp);
|
||||
const double _yp = (sin * xp) + (cos * yp);
|
||||
xp = _xp;
|
||||
yp = _yp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline QPoint3D rotated(const double &phi, const Axis &axis) const
|
||||
{
|
||||
QPoint3D p(*this);
|
||||
p.rotate(phi, axis);
|
||||
return p;
|
||||
}
|
||||
|
||||
inline double distanceToLine(const QPoint3D &point, const QPoint3D &direction) const;
|
||||
inline double distanceToPlane(const QPoint3D &plane, const QPoint3D &normal) const;
|
||||
inline double distanceToPlane(const QPoint3D &plane1, const QPoint3D &plane2, const QPoint3D &plane3) const;
|
||||
inline double distanceToPoint(const QPoint3D &point) const;
|
||||
|
||||
friend Q_DECL_CONSTEXPR inline bool operator==(const QPoint3D &p1, const QPoint3D &p2);
|
||||
friend Q_DECL_CONSTEXPR inline bool operator!=(const QPoint3D &p1, const QPoint3D &p2);
|
||||
friend Q_DECL_CONSTEXPR inline QPoint3D operator +(const QPoint3D &p1, const QPoint3D &p2)
|
||||
{
|
||||
return QPoint3D(p1.xp + p2.xp, p1.yp + p2.yp, p1.zp + p2.zp);
|
||||
}
|
||||
|
||||
friend Q_DECL_CONSTEXPR inline QPoint3D operator -(const QPoint3D &p1, const QPoint3D &p2)
|
||||
{
|
||||
return QPoint3D(p1.xp - p2.xp, p1.yp - p2.yp, p1.zp - p2.zp);
|
||||
}
|
||||
|
||||
friend Q_DECL_CONSTEXPR inline QPoint3D operator *(const QPoint3D &p1, double factor)
|
||||
{
|
||||
return QPoint3D(p1.xp * factor, p1.yp * factor, p1.zp * factor);
|
||||
}
|
||||
friend Q_DECL_CONSTEXPR inline QPoint3D operator *(double factor, const QPoint3D &p1)
|
||||
{
|
||||
return QPoint3D(p1.xp * factor, p1.yp * factor, p1.zp * factor);
|
||||
}
|
||||
|
||||
friend Q_DECL_CONSTEXPR inline QPoint3D operator /(const QPoint3D &p1, double factor)
|
||||
{
|
||||
return QPoint3D(p1.xp / factor, p1.yp / factor, p1.zp / factor);
|
||||
}
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator +=(const QPoint3D &o)
|
||||
{
|
||||
xp += o.xp;
|
||||
yp += o.yp;
|
||||
zp += o.zp;
|
||||
return *this;
|
||||
}
|
||||
Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator -=(const QPoint3D &o)
|
||||
{
|
||||
xp -= o.xp;
|
||||
yp -= o.yp;
|
||||
zp -= o.zp;
|
||||
return *this;
|
||||
}
|
||||
Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator *=(double factor)
|
||||
{
|
||||
xp *= factor;
|
||||
yp *= factor;
|
||||
zp *= factor;
|
||||
return *this;
|
||||
}
|
||||
Q_DECL_RELAXED_CONSTEXPR inline QPoint3D &operator /=(double divisor)
|
||||
{
|
||||
xp /= divisor;
|
||||
yp /= divisor;
|
||||
zp /= divisor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR static inline double dotProduct(const QPoint3D& v1, const QPoint3D& v2)
|
||||
{
|
||||
return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR static inline QPoint3D crossProduct(const QPoint3D& v1, const QPoint3D& v2)
|
||||
{
|
||||
return QPoint3D(v1.yp * v2.zp - v1.zp * v2.yp,
|
||||
v1.zp * v2.xp - v1.xp * v2.zp,
|
||||
v1.xp * v2.yp - v1.yp * v2.xp);
|
||||
}
|
||||
|
||||
static inline QPoint3D normal(const QPoint3D &v1, const QPoint3D &v2)
|
||||
{
|
||||
return crossProduct(v1, v2).normalized();
|
||||
}
|
||||
static inline QPoint3D normal(const QPoint3D &v1, const QPoint3D &v2, const QPoint3D &v3)
|
||||
{
|
||||
return crossProduct((v2 - v1), (v3 - v1)).normalized();
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QPoint3D& v1, const QPoint3D& v2);
|
||||
|
||||
private:
|
||||
double xp = 0;
|
||||
double yp = 0;
|
||||
double zp = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
LTSUTILSCORE_EXPORT QDataStream &operator<<(QDataStream &s, const QPoint3D &p);
|
||||
LTSUTILSCORE_EXPORT QDataStream &operator>>(QDataStream &s, QPoint3D &p);
|
||||
|
||||
|
||||
|
||||
|
||||
Q_DECL_CONSTEXPR inline QPoint3D::QPoint3D(double xpos, double ypos, double zpos) : xp(xpos), yp(ypos), zp(zpos) { }
|
||||
|
||||
inline bool QPoint3D::isNull() const
|
||||
{
|
||||
return qIsNull(xp) && qIsNull(yp) && qIsNull(zp);
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline double QPoint3D::x() const
|
||||
{
|
||||
return xp;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline double QPoint3D::y() const
|
||||
{
|
||||
return yp;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline double QPoint3D::z() const
|
||||
{
|
||||
return zp;
|
||||
}
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline void QPoint3D::setX(double xpos)
|
||||
{
|
||||
xp = xpos;
|
||||
}
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline void QPoint3D::setY(double ypos)
|
||||
{
|
||||
yp = ypos;
|
||||
}
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline void QPoint3D::setZ(double zpos)
|
||||
{
|
||||
zp = zpos;
|
||||
}
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline double &QPoint3D::rx()
|
||||
{
|
||||
return xp;
|
||||
}
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline double &QPoint3D::ry()
|
||||
{
|
||||
return yp;
|
||||
}
|
||||
|
||||
Q_DECL_RELAXED_CONSTEXPR inline double &QPoint3D::rz()
|
||||
{
|
||||
return zp;
|
||||
}
|
||||
|
||||
|
||||
Q_DECL_CONSTEXPR inline QPointF QPoint3D::toPointF() const
|
||||
{
|
||||
return QPointF(xp, yp);
|
||||
}
|
||||
|
||||
|
||||
Q_DECL_CONSTEXPR inline bool operator==(const QPoint3D &p1, const QPoint3D &p2)
|
||||
{
|
||||
return p1.xp == p2.xp && p1.yp == p2.yp && p1.zp == p2.zp;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline bool operator!=(const QPoint3D &p1, const QPoint3D &p2)
|
||||
{
|
||||
return p1.xp != p2.xp || p1.yp != p2.yp || p1.zp != p2.zp;
|
||||
}
|
||||
|
||||
Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QPoint3D& v1, const QPoint3D& v2)
|
||||
{
|
||||
return ::qFuzzyCompare(v1.xp, v2.xp) &&
|
||||
::qFuzzyCompare(v1.yp, v2.yp) &&
|
||||
::qFuzzyCompare(v1.zp, v2.zp);
|
||||
}
|
||||
|
||||
inline std::ostream& operator<< (std::ostream& stream, const QPoint3D& pos)
|
||||
{
|
||||
stream << "(" << pos.x() << "," << pos.y() << "," << pos.z() << ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_TYPEINFO(LtsUtils::QPoint3D, Q_MOVABLE_TYPE);
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
|
||||
#include "scoped_connection.h"
|
||||
#include "connection.h"
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
ScopedConnection::ScopedConnection(IConnection *connection, Qt::ConnectionType connType)
|
||||
: m_conn(connection)
|
||||
{
|
||||
Q_ASSERT(connection);
|
||||
m_conn->connect(connType);
|
||||
}
|
||||
|
||||
ScopedConnection::~ScopedConnection()
|
||||
{
|
||||
m_conn->disconnect();
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
#include <Qt>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
class IConnection;
|
||||
|
||||
|
||||
|
||||
class LTSUTILSCORE_EXPORT ScopedConnection
|
||||
{
|
||||
public:
|
||||
explicit ScopedConnection(IConnection *connection, Qt::ConnectionType = Qt::AutoConnection);
|
||||
~ScopedConnection();
|
||||
|
||||
private:
|
||||
IConnection *m_conn;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
|
||||
|
||||
|
||||
#include "sfm_bytemessage_converter.h"
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
|
||||
|
||||
static constexpr unsigned char XON = 0x11;
|
||||
static constexpr unsigned char XOFF = 0x13;
|
||||
static constexpr unsigned char STX = 0x02;
|
||||
static constexpr unsigned char ETX = 0x03;
|
||||
static constexpr unsigned char SUBST = 0x80;
|
||||
|
||||
static constexpr unsigned char SUB_XON = 0x91;
|
||||
static constexpr unsigned char SUB_XOFF = 0x93;
|
||||
static constexpr unsigned char SUB_STX = 0x82;
|
||||
static constexpr unsigned char SUB_ETX = 0x83;
|
||||
static constexpr unsigned char SUB_SUBST = 0x00;
|
||||
|
||||
static constexpr int HEADER_SIZE = 3;
|
||||
static constexpr int CHECKSUM_SIZE = 1;
|
||||
static constexpr int MINIMUM_BYTE_SIZE = 1 + HEADER_SIZE + CHECKSUM_SIZE + 1;
|
||||
|
||||
|
||||
|
||||
static SfmMessage getSfmMessage(const QByteArray &p_data)
|
||||
{
|
||||
|
||||
unsigned short id = 0;
|
||||
id |= (((unsigned short)p_data[0]) & 0x7F) << 8;
|
||||
id |= (((unsigned short)p_data[1]) & 0xFF) << 0;
|
||||
|
||||
SfmMessage msg;
|
||||
msg.setId(id);
|
||||
msg.setRtr(p_data[0]&0x80);
|
||||
msg.setPayload(p_data.right(p_data.size() - HEADER_SIZE));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static QByteArray getHeader(const SfmMessage &p_msg)
|
||||
{
|
||||
QByteArray header(2, Qt::Uninitialized);
|
||||
|
||||
header[0] = ((p_msg.id() >> 8) & 0x7f) | (p_msg.rtr() << 7);
|
||||
|
||||
header[1] = (p_msg.id() >> 0) & 0xff;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline char getChecksum(const QByteArray &p_data)
|
||||
{
|
||||
char sum = 0;
|
||||
|
||||
|
||||
for (const char c : p_data)
|
||||
sum += c;
|
||||
|
||||
|
||||
return sum ^ 0xff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static QByteArray removeChecksum(const QByteArray &p_data)
|
||||
{
|
||||
QByteArray withoutChecksum(p_data);
|
||||
ConversionError error ("Checksum error");
|
||||
|
||||
if (p_data.size() >= CHECKSUM_SIZE)
|
||||
{
|
||||
withoutChecksum.chop(1);
|
||||
|
||||
|
||||
if (p_data.at(p_data.size() - 1) != getChecksum(withoutChecksum))
|
||||
throw error;
|
||||
}
|
||||
else
|
||||
throw error;
|
||||
|
||||
return withoutChecksum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static QByteArray maskData(const QByteArray &p_data)
|
||||
{
|
||||
QByteArray maskData;
|
||||
maskData.reserve(p_data.size() * 8 / 6);
|
||||
|
||||
for (const unsigned char byte : p_data)
|
||||
{
|
||||
switch (byte)
|
||||
{
|
||||
case XON:
|
||||
maskData.append(SUBST);
|
||||
maskData.append(SUB_XON);
|
||||
break;
|
||||
case XOFF:
|
||||
maskData.append(SUBST);
|
||||
maskData.append(SUB_XOFF);
|
||||
break;
|
||||
case STX:
|
||||
maskData.append(SUBST);
|
||||
maskData.append(SUB_STX);
|
||||
break;
|
||||
case ETX:
|
||||
maskData.append(SUBST);
|
||||
maskData.append(SUB_ETX);
|
||||
break;
|
||||
case SUBST:
|
||||
maskData.append(SUBST);
|
||||
maskData.append(static_cast<char>(SUB_SUBST));
|
||||
break;
|
||||
default:
|
||||
maskData.append(byte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return maskData;
|
||||
}
|
||||
|
||||
static bool mustBeMasked(const unsigned char byte)
|
||||
{
|
||||
switch (byte)
|
||||
{
|
||||
case XON:
|
||||
case XOFF:
|
||||
case STX:
|
||||
case ETX:
|
||||
case SUBST:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int lenMaskedData(const QByteArray &data)
|
||||
{
|
||||
int ret = data.length();
|
||||
const unsigned char upperLen = ((data.length() & 0xff00) >> 8);
|
||||
const unsigned char lowerLen = ((data.length() & 0x00ff) >> 0);
|
||||
if (mustBeMasked(upperLen))
|
||||
++ret;
|
||||
if (mustBeMasked(lowerLen))
|
||||
++ret;
|
||||
for (const unsigned char byte : data)
|
||||
{
|
||||
if (mustBeMasked(byte))
|
||||
++ret;
|
||||
}
|
||||
if (mustBeMasked(getChecksum(data)))
|
||||
++ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static QByteArray unmaskData(const QByteArray &p_data)
|
||||
{
|
||||
QByteArray unmaskData;
|
||||
unmaskData.reserve(p_data.size());
|
||||
|
||||
for (int position = 0; position < p_data.size(); ++position)
|
||||
{
|
||||
unsigned char byte = p_data.at(position);
|
||||
if (byte == SUBST)
|
||||
{
|
||||
++position;
|
||||
byte = p_data.at(position);
|
||||
|
||||
switch (byte)
|
||||
{
|
||||
case SUB_XON: unmaskData.append(XON); break;
|
||||
case SUB_XOFF: unmaskData.append(XOFF); break;
|
||||
case SUB_STX: unmaskData.append(STX); break;
|
||||
case SUB_ETX: unmaskData.append(ETX); break;
|
||||
case SUB_SUBST: unmaskData.append(SUBST); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
else
|
||||
unmaskData.append(byte);
|
||||
}
|
||||
return unmaskData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static QByteArray addFrame(const QByteArray &p_data)
|
||||
{
|
||||
QByteArray ret;
|
||||
ret.reserve(2 + p_data.size());
|
||||
ret.append(STX);
|
||||
ret.append(p_data);
|
||||
ret.append(ETX);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static QByteArray removeFrame(const QByteArray &p_data)
|
||||
{
|
||||
|
||||
if (p_data[0] == STX && p_data[p_data.size() - 1] == ETX)
|
||||
{
|
||||
|
||||
return p_data.mid(1, p_data.size() - 2);
|
||||
}
|
||||
else
|
||||
throw ConversionError("Framing Error");
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void checkLength(const QByteArray &p_data)
|
||||
{
|
||||
if (p_data.size() < HEADER_SIZE)
|
||||
throw ConversionError("Telegram too short");
|
||||
|
||||
unsigned char length = p_data[HEADER_SIZE - 1];
|
||||
if (length != p_data.size() - HEADER_SIZE)
|
||||
throw ConversionError("Telegram length does not match");
|
||||
}
|
||||
|
||||
QByteArray byteStreamFromSfmMessage(const SfmMessage &message)
|
||||
{
|
||||
QByteArray byteData(getHeader(message));
|
||||
|
||||
byteData.append(message.payload().size());
|
||||
byteData.append(message.payload());
|
||||
byteData.append(getChecksum(byteData));
|
||||
|
||||
return addFrame(maskData(byteData));
|
||||
}
|
||||
|
||||
SfmMessage byteStreamToSfmMessage(const QByteArray &datastream)
|
||||
{
|
||||
if (datastream.size() < MINIMUM_BYTE_SIZE)
|
||||
throw ConversionError("Can't convert an QByteArray that hasn't the minimum size!");
|
||||
|
||||
QByteArray checkMsg;
|
||||
|
||||
|
||||
checkMsg = removeFrame(datastream);
|
||||
checkMsg = unmaskData(checkMsg);
|
||||
|
||||
checkMsg = removeChecksum(checkMsg);
|
||||
|
||||
checkLength(checkMsg);
|
||||
|
||||
|
||||
return getSfmMessage(checkMsg);
|
||||
}
|
||||
|
||||
QByteArray byteStreamFromEscMessage(const QByteArray &message)
|
||||
{
|
||||
QByteArray byteData = message;
|
||||
if (message.size() == 1)
|
||||
{
|
||||
|
||||
byteData.append(message);
|
||||
}
|
||||
else
|
||||
byteData.append(getChecksum(byteData));
|
||||
|
||||
byteData = maskData(byteData);
|
||||
byteData = addFrame(byteData);
|
||||
|
||||
return byteData;
|
||||
}
|
||||
|
||||
QByteArray byteStreamToEscMessage(const QByteArray &datastream)
|
||||
{
|
||||
if (datastream.size() < 2)
|
||||
return QByteArray("ERROR");
|
||||
|
||||
QByteArray checkMsg;
|
||||
|
||||
|
||||
checkMsg = removeFrame(datastream);
|
||||
checkMsg = unmaskData(checkMsg);
|
||||
|
||||
|
||||
checkMsg.chop(1);
|
||||
return checkMsg;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LtsUtilsCore/ltsutilscore_export.h>
|
||||
#include <QByteArray>
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
class LTSUTILSCORE_EXPORT SfmMessage
|
||||
{
|
||||
public:
|
||||
unsigned int id() const { return m_id; }
|
||||
void setId(unsigned int id) { m_id = id; }
|
||||
|
||||
bool rtr() const { return m_rtr; }
|
||||
void setRtr(bool rtr) { m_rtr = rtr; }
|
||||
|
||||
QByteArray payload() const { return m_payload; }
|
||||
void setPayload(const QByteArray &payload) { m_payload = payload; }
|
||||
|
||||
bool operator==(const SfmMessage &msg) const
|
||||
{
|
||||
return (m_id == msg.id() &&
|
||||
m_rtr == msg.rtr() &&
|
||||
m_payload == msg.payload());
|
||||
}
|
||||
|
||||
protected:
|
||||
unsigned int m_id = 0;
|
||||
bool m_rtr = false;
|
||||
QByteArray m_payload;
|
||||
};
|
||||
|
||||
|
||||
class ConversionError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
|
||||
|
||||
LTSUTILSCORE_EXPORT QByteArray byteStreamFromSfmMessage(const SfmMessage &message);
|
||||
|
||||
|
||||
|
||||
LTSUTILSCORE_EXPORT SfmMessage byteStreamToSfmMessage(const QByteArray &datastream);
|
||||
|
||||
|
||||
|
||||
LTSUTILSCORE_EXPORT QByteArray byteStreamFromEscMessage(const QByteArray &message);
|
||||
|
||||
|
||||
|
||||
LTSUTILSCORE_EXPORT QByteArray byteStreamToEscMessage(const QByteArray &datastream);
|
||||
|
||||
|
||||
|
||||
LTSUTILSCORE_EXPORT int lenMaskedData(const QByteArray &data);
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <QVariant>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <ngltools/ngltools_dtg.h>
|
||||
|
||||
|
||||
namespace LtsUtils
|
||||
{
|
||||
|
||||
inline bool lessThan(const QVariant &var1, const QVariant &var2)
|
||||
{
|
||||
if (var1.canConvert(QMetaType::QString) && var2.canConvert(QMetaType::QString))
|
||||
{
|
||||
QString str1(var1.toString());
|
||||
QString str2(var2.toString());
|
||||
|
||||
bool k1 = false;
|
||||
double d1 = str1.toDouble(&k1);
|
||||
|
||||
if (!k1)
|
||||
return str1 < str2;
|
||||
|
||||
bool k2 = false;
|
||||
double d2 = str2.toDouble(&k2);
|
||||
|
||||
if (!k2)
|
||||
return str1 < str2;
|
||||
|
||||
return d1 < d2;
|
||||
}
|
||||
else if (var1.canConvert(QMetaType::Double) && var2.canConvert(QMetaType::Double))
|
||||
return var1.toDouble() < var2.toDouble();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user