Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+87
@@ -0,0 +1,87 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
#
|
||||
# --- Executable svcWirelessLocationSystem (svcWLS) -------------------------------------------------
|
||||
#
|
||||
SET(xml_FILE wls_service.xml)
|
||||
|
||||
SET(SRCS_exe
|
||||
dds_handler.cpp
|
||||
dds_handler.h
|
||||
ism_handler.cpp
|
||||
ism_handler.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
main.cpp
|
||||
main_handler.cpp
|
||||
main_handler.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
web_socket_handler.cpp
|
||||
web_socket_handler.h
|
||||
wls_handler.cpp
|
||||
wls_handler.h
|
||||
wls_participant.cpp
|
||||
wls_participant.h
|
||||
wls_participant_handler.cpp
|
||||
wls_participant_handler.h
|
||||
wls_sfm_message_converter.cpp
|
||||
wls_sfm_message_converter.h
|
||||
wls_sfm_message.cpp
|
||||
wls_sfm_message.h
|
||||
)
|
||||
# make sure xml file is properly installed
|
||||
CODEGEN_HANDLE_SVC_XML(SRCS_exe ${xml_FILE})
|
||||
|
||||
NGL_ADD_SVC_ICON(SRCS_exe)
|
||||
NGL_ADD_EXECUTABLE(AAR SV svcWirelessLocationSystem ${SRCS_exe})
|
||||
NGL_TARGET_LINK_LIBRARIES(svcWirelessLocationSystem
|
||||
Qt5::Core
|
||||
Qt5::Network
|
||||
Qt5::WebSockets
|
||||
svcCoreClient
|
||||
svcIsmClient
|
||||
ddsconnector
|
||||
SfmConverter
|
||||
LtsCommandLineParser
|
||||
LtsCoordTrans
|
||||
LtsUtilsCore
|
||||
NGLLogger
|
||||
nglcoreapplication
|
||||
nglsettings
|
||||
ngltools
|
||||
tabTelegrams
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcWirelessLocationSystem)
|
||||
NGL_INSTALL(AAR SV FILES log4cxx_wls.props DESTINATION bin)
|
||||
NGL_ADD_TARGET_LINK(svcWirelessLocationSystem pWLS)
|
||||
|
||||
NGL_ADD_EXECUTABLE(AAR SV wlsJsonClientTest wls_json_client_test.cpp wls_json_client_test.h)
|
||||
NGL_TARGET_LINK_LIBRARIES(wlsJsonClientTest
|
||||
Qt5::Core
|
||||
Qt5::WebSockets
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(SfmConverter AAR SV SHARED
|
||||
logger.cpp
|
||||
logger.h
|
||||
wls_sfm_message_converter.cpp
|
||||
wls_sfm_message_converter.h
|
||||
wls_sfm_message.cpp
|
||||
wls_sfm_message.h
|
||||
)
|
||||
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(SfmConverter
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(SfmConverter
|
||||
Qt5::Core
|
||||
LtsUtilsCore
|
||||
NGLLogger
|
||||
)
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* DdsHandler for DDS
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#include "dds_handler.h"
|
||||
|
||||
#include <DDS/Core/pacemaker.h>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
DdsHandler::DdsHandler(int domainId, int exerciseId)
|
||||
: m_ddsConnector(new DDSConnector::DdsConnector(domainId, IDL::PartitionType::Global, exerciseId))
|
||||
, m_logger(getLogger("DdsHandler"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "DdsHandler(" << domainId << ") for partition: " << m_ddsConnector->getDefaultPartition();
|
||||
}
|
||||
|
||||
bool DdsHandler::init(const QStringList &qosFileList)
|
||||
{
|
||||
if (!m_ddsConnector->init(qosFileList))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error initializing dds connections - aborting";
|
||||
return false;
|
||||
}
|
||||
|
||||
// default initialize Publisher and Subscriber
|
||||
m_ddsConnector->initPublisherSubscriber();
|
||||
|
||||
// Pacemaker
|
||||
m_pacemaker = std::make_shared<Core::Pacemaker>(m_ddsConnector);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* DdsHandler for DDS
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class Pacemaker;
|
||||
}
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class DdsHandler
|
||||
{
|
||||
public:
|
||||
DdsHandler(int domainId, int exerciseId);
|
||||
|
||||
bool init(const QStringList &qosFileList = QStringList());
|
||||
|
||||
std::shared_ptr<Core::Pacemaker> pacemaker() const
|
||||
{
|
||||
return m_pacemaker;
|
||||
}
|
||||
|
||||
std::shared_ptr<DDSConnector::DdsConnector> ddsConnector() const
|
||||
{
|
||||
return m_ddsConnector;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
|
||||
std::shared_ptr<Core::Pacemaker> m_pacemaker;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* IsmHandler for WirelessLocationSystem
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#include "ism_handler.h"
|
||||
#include "main_handler.h"
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
IsmHandler::IsmHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_logger(getLogger("IsmHandler"))
|
||||
{}
|
||||
|
||||
bool IsmHandler::init(MainHandler *mainHandler)
|
||||
{
|
||||
if (m_dtcIsm)
|
||||
return true;
|
||||
|
||||
m_dtcIsm = InfrastructureManagement::Interface::Dtc::create(mainHandler->ddsHandler()->ddsConnector());
|
||||
connect(m_dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged,
|
||||
this, &IsmHandler::onMainStructureElementChanged);
|
||||
|
||||
if (!m_dtcIsm->enableMainStructureElement())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Can not read data from ISM";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IsmHandler::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType)
|
||||
{
|
||||
switch (modType)
|
||||
{
|
||||
case DataModification::eAdded:
|
||||
case DataModification::eModified:
|
||||
{
|
||||
m_mainStructureElements[data.id()] = data;
|
||||
if (data.externalWlsId() != 0)
|
||||
m_externalWlsId2MseId[data.externalWlsId()] = data.id();
|
||||
QMap<int, int> roomNumber2roomId;
|
||||
for (const auto &element : data.elements())
|
||||
{
|
||||
if (element.structureElementTypeId() != InfrastructureManagement::StructureElementType::Room)
|
||||
continue;
|
||||
roomNumber2roomId[element.mountingPointId()] = element.id();
|
||||
}
|
||||
m_mseId2RoomId[data.id()] = roomNumber2roomId;
|
||||
break;
|
||||
}
|
||||
case DataModification::eRemoved:
|
||||
{
|
||||
m_mainStructureElements.remove(data.id());
|
||||
m_mseId2RoomId.remove(data.id());
|
||||
for (auto it = m_externalWlsId2MseId.begin(); it != m_externalWlsId2MseId.end(); ++it)
|
||||
{
|
||||
if (it.value() == data.id())
|
||||
{
|
||||
m_externalWlsId2MseId.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* IsmHandler for WirelessLocationSystem
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <InfrastructureManagement/ism.hpp>
|
||||
#include <InfrastructureManagement/ism_dtc.h>
|
||||
#include <LtsUtilsCore/data_modification_enum.h>
|
||||
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class MainHandler;
|
||||
class IsmHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit IsmHandler(QObject *parent = nullptr);
|
||||
|
||||
bool init(MainHandler *mainHandler);
|
||||
|
||||
int buildingIdFromExternalWlsId(int externalWlsId) const { return m_externalWlsId2MseId.value(externalWlsId, -1); }
|
||||
int roomIdFromRoomNumber(int buildingId, int roomNumber) const { return m_mseId2RoomId.value(buildingId).value(roomNumber, -1); }
|
||||
|
||||
private Q_SLOTS:
|
||||
void onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType);
|
||||
|
||||
private:
|
||||
// QMap<mseId, MainStructureElement>
|
||||
QHash<int, InfrastructureManagement::MainStructureElement> m_mainStructureElements;
|
||||
// QMap<externalWlsId, mseId>
|
||||
QHash<int, int> m_externalWlsId2MseId;
|
||||
// QMap<mseId, QMap<roomNumber, roomId>>
|
||||
QHash<int, QMap<int, int>> m_mseId2RoomId;
|
||||
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_dtcIsm;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
log4j.reset=true
|
||||
log4j.rootLogger=WARN, file
|
||||
|
||||
# Service: WirelessLocationSystem
|
||||
###########################################################
|
||||
|
||||
#log4j.logger.WLS=DEBUG
|
||||
#log4j.logger.WLS.DdsHandler=INFO
|
||||
#log4j.logger.WLS.IsmHandler=INFO
|
||||
#log4j.logger.WLS.MainHandler=INFO
|
||||
#log4j.logger.WLS.WebSocketHandler=WARN
|
||||
#log4j.logger.WLS.WlsHandler=INFO
|
||||
|
||||
|
||||
# File
|
||||
###########################################################
|
||||
log4j.appender.file=Lts::RollingFileAppender
|
||||
log4j.appender.file.MaxFileSize=1MB
|
||||
log4j.appender.file.MaxBackupIndex=9
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
|
||||
|
||||
|
||||
# Appnd_Console
|
||||
###########################################################
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
|
||||
|
||||
|
||||
# Appnd_Syslog
|
||||
###########################################################
|
||||
log4j.appender.syslog=org.apache.log4j.SyslogAppender
|
||||
log4j.appender.syslog.Threshold=ERROR
|
||||
log4j.appender.syslog.SysLogHost=localhost
|
||||
log4j.appender.syslog.Facility=LOCAL7
|
||||
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
|
||||
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
|
||||
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
|
||||
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
###########################################################
|
||||
# 1) Available Levels in LOG4CXX Version 0.10.0:
|
||||
# TRACE,
|
||||
# DEBUG,
|
||||
# INFO,
|
||||
# WARN,
|
||||
# ERROR
|
||||
# FATAL
|
||||
#
|
||||
# d = date/time
|
||||
# p = priority (Level)
|
||||
# c = category/channel
|
||||
# m = message
|
||||
# F = file
|
||||
# L = line
|
||||
# n = platform dependent line separator character or characters.
|
||||
# ....
|
||||
#
|
||||
# Examples for console:
|
||||
# a)
|
||||
# %d %p %c #%m# (%.50F:%L)%n
|
||||
# date/time
|
||||
# priority
|
||||
# category/channel
|
||||
# #message#
|
||||
# (Maximal 50Character des Filename von rechts:Line)
|
||||
# line separator
|
||||
#
|
||||
# b)
|
||||
# %d (%.15F:%L) %-5p %c |#| %m%n
|
||||
# date/time
|
||||
# (Maximal 15Character des Filename von rechts:Line)
|
||||
# priority, Right pad with spaces if the priority name is less than 5 characters long.
|
||||
# category/channel
|
||||
# message
|
||||
# line separator
|
||||
#
|
||||
# Examples for files:
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Building Config - logging facility
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "WLS.";
|
||||
std::string loggerName;
|
||||
const size_t len = strlen(name);
|
||||
loggerName.reserve(sizeof(ns) + len);
|
||||
loggerName.append(ns, sizeof(ns) - 1);
|
||||
loggerName.append(name, len);
|
||||
|
||||
try
|
||||
{
|
||||
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
|
||||
getNGLLogger(loggerName);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
fprintf(stderr, "Error retrieving logger: %s", ex.what());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Logging for TrainingAreaBase
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
using LOGGER_T = NGL::INGLLogger&;
|
||||
|
||||
LOGGER_T getLogger(const char *name);
|
||||
inline LOGGER_T getLogger(const QString &loggerName)
|
||||
{
|
||||
return getLogger(loggerName.toUtf8().constData());
|
||||
}
|
||||
} // namespace WirelessLocationSystem
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* main for TetraRadio
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <LtsCommandLineParser/LtsCommandLineParser.h>
|
||||
#include <QNetworkProxy>
|
||||
#include <memory>
|
||||
#include "main_handler.h"
|
||||
#include "logger.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <LtsUtilsCore/sfm_bytemessage_converter.h>
|
||||
#include "wls_sfm_message.h"
|
||||
#include "wls_sfm_message_converter.h"
|
||||
|
||||
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
|
||||
{
|
||||
public:
|
||||
CmdLineParser()
|
||||
: LtsCommandLineParser()
|
||||
{
|
||||
addOption({ QStringList() << QLatin1String("c") << QLatin1String("checkData"),
|
||||
QLatin1String("Testdata"),
|
||||
QLatin1String("data to check as hex (e.g. '02044c221f8091660bb300001a31313131313131313131313131313131313131313131313131312503'"),
|
||||
QString()
|
||||
});
|
||||
}
|
||||
|
||||
bool isCheckData() const { return isSet(QLatin1String("c")); }
|
||||
QByteArray dataToCheck() const { return value(QLatin1String("c")).toUtf8(); }
|
||||
};
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
bool sendBinarySfmData(const QByteArray &ba, int sendCounter)
|
||||
{
|
||||
int chunkLen = 0;
|
||||
int chunkCount = 1;
|
||||
|
||||
if (!WlsParticipant::calcChunkCountAndLen(ba, chunkCount, chunkLen))
|
||||
{
|
||||
fprintf(stderr, "Chunk count is not ok - data size: %d\n", ba.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
int fragmentNumber = 1;
|
||||
int iPos = 0;
|
||||
while (iPos < ba.size())
|
||||
{
|
||||
const SfmMsgTetra_tx tx(sendCounter,
|
||||
chunkCount,
|
||||
fragmentNumber,
|
||||
ba.mid(iPos, chunkLen));
|
||||
const auto payload(SfmMessageConverter::generateTxMessage(tx));
|
||||
const unsigned int fragmentParts = ((payload.size() + WlsHandler::s_maxLenTransparentData - 1) / WlsHandler::s_maxLenTransparentData);
|
||||
if (fragmentParts != 1)
|
||||
fprintf(stderr, "Calculation mismatch - only expected one part, got %d\n", fragmentParts);
|
||||
|
||||
SfmMsgTetra_rx rx;
|
||||
rx.setPayload(tx.getPayload());
|
||||
fprintf(stdout, "Output: len: %d(%d), %s, sendCounter: %d(0x%02x), is fragmented: %d, fragment: %d/%d\n",
|
||||
payload.size(), tx.getPayload().size(), payload.data(),
|
||||
rx.sendCounter(), rx.sendCounter(), rx.isFragmented(), rx.fragmentNumber(), rx.fragmentCount());
|
||||
|
||||
++sendCounter;
|
||||
++fragmentNumber;
|
||||
iPos += chunkLen;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int checkData(const QByteArray &data)
|
||||
{
|
||||
const QByteArray ba(QByteArray::fromHex(data));
|
||||
try
|
||||
{
|
||||
const auto msg = LtsUtils::byteStreamToSfmMessage(ba);
|
||||
fprintf(stdout, "Message payload: len: %d, %s\n", msg.payload().size(), msg.payload().toHex().data());
|
||||
if (msg.id() == int(SfmMessage::MsgId::TetraMessage))
|
||||
{
|
||||
WirelessLocationSystem::SfmMsgTetra_rx rx;
|
||||
rx.setPayload(msg.payload());
|
||||
fprintf(stdout, "SfmMsgTetra payload: len: %d, %s, ", rx.payload().size(), rx.payload().toHex().data());
|
||||
fprintf(stdout, "sendCounter: %d(0x%02x), is fragmented: %d, fragment: %d/%d\n",
|
||||
rx.sendCounter(), rx.sendCounter(), rx.isFragmented(), rx.fragmentNumber(), rx.fragmentCount());
|
||||
sendBinarySfmData(rx.payload(), 0x02); // 0x02 -> a value which has to be substituted
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fprintf(stderr, "Could not decode message: %s\n", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace WirelessLocationSystem
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NGLCoreApplication app(argc, argv);
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
|
||||
int iRet = -1;
|
||||
CmdLineParser options;
|
||||
if (!options.init())
|
||||
return iRet;
|
||||
app.createLogger("WLS", 0);
|
||||
|
||||
if (options.isCheckData())
|
||||
return WirelessLocationSystem::checkData(options.dataToCheck());
|
||||
|
||||
WirelessLocationSystem::Settings::setIniFile(options.iniFile());
|
||||
|
||||
{
|
||||
std::unique_ptr<WirelessLocationSystem::MainHandler> mainHandler(new WirelessLocationSystem::MainHandler);
|
||||
if (mainHandler->init(options.domainId()))
|
||||
{
|
||||
STARTUP_LOG(app.baseLogger());
|
||||
iRet = app.exec();
|
||||
SHUTDOWN_LOG(app.baseLogger());
|
||||
}
|
||||
}
|
||||
|
||||
return iRet;
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* MainHandler for TetraRadio
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#include "main_handler.h"
|
||||
#include "settings.h"
|
||||
#include <DDS/Core/pacemaker.h>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
MainHandler::MainHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_ddsHandler(nullptr)
|
||||
, m_logger(getLogger("MainHandler"))
|
||||
{}
|
||||
|
||||
MainHandler::~MainHandler()
|
||||
{
|
||||
m_wlsParticipantHandler = nullptr;
|
||||
m_wlsHandler = nullptr;
|
||||
m_wsHandler = nullptr;
|
||||
m_ismHandler = nullptr;
|
||||
m_ddsHandler = nullptr;
|
||||
}
|
||||
|
||||
bool MainHandler::init(int domainId)
|
||||
{
|
||||
const int exerciseId = 0;
|
||||
// initialize DDS
|
||||
m_ddsHandler = std::make_shared<DdsHandler>(domainId, exerciseId);
|
||||
if (!m_ddsHandler->init())
|
||||
return false;
|
||||
|
||||
m_wlsParticipantHandler = std::make_shared<WlsParticipantHandler>();
|
||||
m_wsHandler = std::make_shared<WebSocketHandler>();
|
||||
m_ismHandler = std::make_shared<IsmHandler>();
|
||||
m_wlsHandler = std::make_shared<WlsHandler>();
|
||||
|
||||
if (!m_ismHandler->init(this))
|
||||
return false;
|
||||
|
||||
if (!m_wlsHandler->init(m_wlsParticipantHandler.get(), // TODO: if using shared pointers, then *use* them
|
||||
m_wsHandler.get(),
|
||||
Settings::wlsListenPort()))
|
||||
return false;
|
||||
|
||||
if (!m_wsHandler->init(m_wlsHandler,
|
||||
m_wlsParticipantHandler,
|
||||
m_ismHandler,
|
||||
Settings::webSocketListenPort()))
|
||||
return false;
|
||||
m_wlsParticipantHandler->init(m_wsHandler.get(), // TODO: if using shared pointers, then *use* them
|
||||
m_wlsHandler.get());
|
||||
|
||||
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::Running);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* MainHandler for TetraRadio
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
#include "dds_handler.h"
|
||||
#include "ism_handler.h"
|
||||
#include "wls_participant_handler.h"
|
||||
#include "web_socket_handler.h"
|
||||
#include "wls_handler.h"
|
||||
#include <InfrastructureManagement/ism_dtc.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class MainHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainHandler(QObject *parent = nullptr);
|
||||
~MainHandler();
|
||||
|
||||
bool init(int domainId);
|
||||
|
||||
std::shared_ptr<DdsHandler> ddsHandler() const
|
||||
{
|
||||
return m_ddsHandler;
|
||||
}
|
||||
std::shared_ptr<IsmHandler> ismHandler() const
|
||||
{
|
||||
return m_ismHandler;
|
||||
}
|
||||
std::shared_ptr<WlsHandler> wlsHandler() const
|
||||
{
|
||||
return m_wlsHandler;
|
||||
}
|
||||
std::shared_ptr<WebSocketHandler> webSocketHandler() const
|
||||
{
|
||||
return m_wsHandler;
|
||||
}
|
||||
std::shared_ptr<WlsParticipantHandler> participantHandler() const
|
||||
{
|
||||
return m_wlsParticipantHandler;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DdsHandler> m_ddsHandler;
|
||||
std::shared_ptr<WlsParticipantHandler> m_wlsParticipantHandler;
|
||||
std::shared_ptr<WlsHandler> m_wlsHandler;
|
||||
std::shared_ptr<WebSocketHandler> m_wsHandler;
|
||||
std::shared_ptr<IsmHandler> m_ismHandler;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace WirelessLocationSystem
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Settings for TetraRadio
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <ngltools/ngltools_filesystem.h>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class NGLSettingsClass : public NGL::Settings
|
||||
{
|
||||
public:
|
||||
NGLSettingsClass()
|
||||
: NGL::Settings(s_iniFile.isEmpty() ? NGL::findProjectConfigFile() : s_iniFile)
|
||||
{}
|
||||
static void setIniFile(const QString &fn)
|
||||
{
|
||||
s_iniFile = fn;
|
||||
}
|
||||
static QString s_iniFile;
|
||||
};
|
||||
QString NGLSettingsClass::s_iniFile;
|
||||
Q_GLOBAL_STATIC(NGLSettingsClass, s)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
NGL::Settings *Settings::settings()
|
||||
{
|
||||
return s();
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Settings::setIniFile(const QString &ini)
|
||||
{
|
||||
NGLSettingsClass::setIniFile(ini);
|
||||
}
|
||||
|
||||
quint16 Settings::wlsListenPort()
|
||||
{
|
||||
return settings()->settings()->value(QStringLiteral("WLS/wlsListenPort"), 12002).toInt();
|
||||
}
|
||||
|
||||
quint16 Settings::webSocketListenPort()
|
||||
{
|
||||
return settings()->settings()->value(QStringLiteral("WLS/webSocketListenPort"), 12346).toInt();
|
||||
}
|
||||
|
||||
QString Settings::wlsMgrsBasePoint()
|
||||
{
|
||||
return settings()->settings()->value(QStringLiteral("WLS/wlsBasePoint"), QStringLiteral("32UMD0000000000000000")).toString();
|
||||
}
|
||||
|
||||
double Settings::wlsZOffset()
|
||||
{
|
||||
return settings()->settings()->value(QStringLiteral("WLS/wlsZOffset"), 0).toDouble();
|
||||
}
|
||||
|
||||
int Settings::deadPosTimeout()
|
||||
{
|
||||
return settings()->settings()->value(QStringLiteral("WLS/deadPosTimeout"), 5000).toInt();
|
||||
}
|
||||
|
||||
double Settings::wlsSquareDistanceMax()
|
||||
{
|
||||
return settings()->settings()->value(QStringLiteral("WLS/squareDistanceMax"), 0.0).toDouble();
|
||||
}
|
||||
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Settings for TetraRadio
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class Settings;
|
||||
}
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
namespace Settings
|
||||
{
|
||||
|
||||
//!< The real NGL::Settings - object
|
||||
NGL::Settings *settings();
|
||||
|
||||
void setIniFile(const QString &ini);
|
||||
|
||||
quint16 wlsListenPort();
|
||||
quint16 webSocketListenPort();
|
||||
double wlsZOffset();
|
||||
int deadPosTimeout();
|
||||
double wlsSquareDistanceMax();
|
||||
QString wlsMgrsBasePoint();
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* Websocket interface to the training area proxy
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-11
|
||||
*/
|
||||
|
||||
#include "web_socket_handler.h"
|
||||
|
||||
#include "ism_handler.h"
|
||||
#include "main_handler.h"
|
||||
#include "settings.h"
|
||||
#include "wls_handler.h"
|
||||
#include "wls_participant_handler.h"
|
||||
|
||||
#include <LtsUtilsCore/dds_vector.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QPointF>
|
||||
#include <QWebSocket>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
const char *exerciseIdProperty = "exerciseId";
|
||||
|
||||
WebSocketHandler::WebSocketHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_webSocketServer(new QWebSocketServer(QStringLiteral("WLS"), QWebSocketServer::NonSecureMode, this))
|
||||
, m_basePoint(LtsCoordTrans::UtmCoordinate::fromMgrs(Settings::wlsMgrsBasePoint()))
|
||||
, m_logger(getLogger("WebSocketHandler"))
|
||||
{}
|
||||
|
||||
WebSocketHandler::~WebSocketHandler()
|
||||
{}
|
||||
|
||||
bool WebSocketHandler::init(std::shared_ptr<WlsHandler> wlsHandler,
|
||||
std::shared_ptr<WlsParticipantHandler> participantHandler,
|
||||
std::shared_ptr<IsmHandler> ismHandler,
|
||||
quint16 listenPort)
|
||||
{
|
||||
if (!m_webSocketServer->listen(QHostAddress::Any, listenPort))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not listen on port " << listenPort << " - aborting" << m_webSocketServer->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
connect(m_webSocketServer, &QWebSocketServer::newConnection,
|
||||
this, &WebSocketHandler::onNewConnection);
|
||||
|
||||
m_wlsHandler = wlsHandler;
|
||||
m_participantHandler = participantHandler;
|
||||
m_ismHandler = ismHandler;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebSocketHandler::onNewConnection()
|
||||
{
|
||||
QWebSocket *ws = m_webSocketServer->nextPendingConnection();
|
||||
|
||||
if (ws == nullptr)
|
||||
return;
|
||||
|
||||
DEBUG_LOG(m_logger) << "New websocket connection from " << ws->peerAddress().toString() << ":" << ws->peerPort();
|
||||
|
||||
connect(ws, &QWebSocket::textMessageReceived,
|
||||
this, &WebSocketHandler::onTextMessageReceived);
|
||||
connect(ws, &QWebSocket::disconnected,
|
||||
ws, &QWebSocket::deleteLater);
|
||||
|
||||
connect(this, &WebSocketHandler::signalBroadcastMessage,
|
||||
ws, [this, ws](const QString &msg, int exerciseId)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "broadcast: exercise property is valid? " << ws->property(exerciseIdProperty).isValid() << " for " << ws->peerPort();
|
||||
|
||||
bool exerciseIdAvailable = false;
|
||||
int wsExerciseId = ws->property(exerciseIdProperty).toInt(&exerciseIdAvailable);
|
||||
|
||||
if (exerciseIdAvailable && wsExerciseId != exerciseId) // NOTE: send for websocket clients that previously didn't set their exercise id beforehand! (e.g. the WLS-Diagnose tool)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "broadcast: websocket has " << wsExerciseId << ", but " << exerciseId << " was requested";
|
||||
return;
|
||||
}
|
||||
|
||||
ws->sendTextMessage(msg);
|
||||
});
|
||||
|
||||
// send wls connection state (to all web sockets, should not hurt much)
|
||||
TrainingAreaBase::TetraIcd::ProxyConnectionState connState;
|
||||
connState.connectedToSystem = m_wlsHandler->isConnected();
|
||||
sendTelegram(&connState, 0);
|
||||
|
||||
for (const WlsParticipant *participant : qAsConst(m_participantHandler->participants()))
|
||||
sendDataCommunicationState(participant);
|
||||
}
|
||||
|
||||
void WebSocketHandler::sendTelegramInternal(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *data,
|
||||
const QString &type, int exerciseId)
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj[QStringLiteral("data")] = data->toJson();
|
||||
obj[QStringLiteral("type")] = type;
|
||||
obj[QStringLiteral("typeId")] = data->id();
|
||||
// if (data->id() != TrainingAreaBase::TetraIcd::TelegramId::BESTAETIGUNG)
|
||||
// obj[QStringLiteral("counter")] = m_sendCounter++;
|
||||
|
||||
const QString jsonStr(QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)));
|
||||
DEBUG_LOG(m_logger) << "jsonStr: " << jsonStr;
|
||||
|
||||
emit signalBroadcastMessage(jsonStr, exerciseId);
|
||||
}
|
||||
|
||||
void WebSocketHandler::sendParticipantConnectionStates(int exerciseId)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::WlsParticipantsConnectionState partConnState;
|
||||
for (const WlsParticipant *p : m_participantHandler->participants())
|
||||
{
|
||||
if (p->equipmentNumber() == 0 ||
|
||||
p->exerciseId() != exerciseId ||
|
||||
p->tagId().isNull())
|
||||
continue;
|
||||
|
||||
partConnState.equipmentNumber = p->equipmentNumber();
|
||||
partConnState.dataCommunicationPossible = p->isDataCommunicationPossible();
|
||||
sendTelegram(&partConnState, exerciseId);
|
||||
}
|
||||
}
|
||||
|
||||
bool WebSocketHandler::sendWlsStateReport(int exerciseId)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::WlsParticipantsStateReport stateReport;
|
||||
stateReport.participantStates.reserve(m_participantHandler->participants().size());
|
||||
|
||||
for (const WlsParticipant *p : m_participantHandler->participants())
|
||||
stateReport.participantStates.push_back(p->wlsParticipantState());
|
||||
|
||||
sendTelegram(&stateReport, exerciseId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebSocketHandler::onTextMessageReceived(const QString &baJson)
|
||||
{
|
||||
using namespace TrainingAreaBase::TetraIcd;
|
||||
|
||||
TRACE_LOG(m_logger) << "WebSocketHandler::onTextMessageReceived: " << baJson;
|
||||
QWebSocket *ws = qobject_cast<QWebSocket*>(sender());
|
||||
|
||||
if (ws == nullptr)
|
||||
return;
|
||||
|
||||
if (baJson.isEmpty())
|
||||
return;
|
||||
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(baJson.toUtf8(), &err));
|
||||
|
||||
if (err.error != QJsonParseError::NoError)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "received invalid json from websocket at " << err.offset << ": " << err.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonObject obj(doc.object());
|
||||
// const QJsonValue sendCounter = obj.value(QStringLiteral("counter"));
|
||||
// if (!sendCounter.isUndefined())
|
||||
// {
|
||||
// if (!sendCounter.isDouble())
|
||||
// return false;
|
||||
// recvCounter = sendCounter.toInt(-1);
|
||||
// DEBUG_LOG(m_logger) << "Incoming telegram with Id " << obj.value(QStringLiteral("type")).toString();
|
||||
// if (recvCounter < 0)
|
||||
// return false;
|
||||
// }
|
||||
|
||||
const QJsonValue typeId(obj.value(QStringLiteral("typeId")));
|
||||
if (!typeId.isDouble())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Can not read type id: " << baJson;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const QJsonObject data(obj.value(QStringLiteral("data")).toObject());
|
||||
try
|
||||
{
|
||||
switch (typeId.toInt())
|
||||
{
|
||||
case Constraints<WlsParticipantsStateRequest>::id(): // 3001 (only used by WLS-Diagnose tool)
|
||||
{
|
||||
sendWlsStateReport(-1); // needs to be -1, otherwise the websocket who subscribed to exercise 0 would receive the answer, too
|
||||
break;
|
||||
}
|
||||
case Constraints<ExerciseStateData>::id(): // 2008
|
||||
{
|
||||
int exerciseId = ExerciseStateData::fromJson(data).exerciseId;
|
||||
ws->setProperty(exerciseIdProperty, exerciseId);
|
||||
DEBUG_LOG(m_logger) << "set exercise id " << exerciseId << " for ws with peer port " << ws->peerPort();
|
||||
sendParticipantConnectionStates(exerciseId);
|
||||
break;
|
||||
}
|
||||
case Constraints<ExerciseEquipmentAssignmentComplete>::id(): // 2012
|
||||
m_participantHandler->exerciseEquipmentAssignmentChanged(ExerciseEquipmentAssignmentComplete::fromJson(data).exerciseEquipmentAssignments);
|
||||
break;
|
||||
case Constraints<WlsBinarySfmData>::id(): // 3003
|
||||
m_participantHandler->handleWlsBinarySfmData(WlsBinarySfmData::fromJson(data));
|
||||
break;
|
||||
default:
|
||||
WARNING_LOG(m_logger) << "Handler for telegram Id " << typeId.toInt()
|
||||
<< " not yet implemented (" << baJson << ")";
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Could not parse json: " << e.what() << " (" << baJson << ")";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketHandler::sendBinarySfmData(const WlsParticipant *participant)
|
||||
{
|
||||
if (!participant->isSfmRxDataAvailable())
|
||||
return;
|
||||
if (participant->equipmentNumber() == 0 || participant->tagId().isNull())
|
||||
return;
|
||||
|
||||
TrainingAreaBase::TetraIcd::WlsBinarySfmData tele;
|
||||
tele.header.equipmentNumber = participant->equipmentNumber();
|
||||
tele.header.timeStamp = participant->lastSeenWlsTelegramTime();
|
||||
LtsUtils::convert(participant->sfmRxData(), tele.payload);
|
||||
sendTelegram(&tele, participant->exerciseId());
|
||||
}
|
||||
|
||||
void WebSocketHandler::sendDataCommunicationState(const WlsParticipant *participant)
|
||||
{
|
||||
if (participant->equipmentNumber() == 0 || participant->tagId().isNull())
|
||||
return;
|
||||
|
||||
TrainingAreaBase::TetraIcd::WlsParticipantsConnectionState tele;
|
||||
tele.equipmentNumber = participant->equipmentNumber();
|
||||
tele.dataCommunicationPossible = participant->isDataCommunicationPossible();
|
||||
sendTelegram(&tele, participant->exerciseId());
|
||||
}
|
||||
|
||||
void WebSocketHandler::sendGpsPositionUsage(const WlsParticipant *participant)
|
||||
{
|
||||
if (!participant->isGpsMode())
|
||||
return;
|
||||
// trigger to notify to not use WlsPositions anymore
|
||||
// see SendToDds::setPosition
|
||||
forceSendPosition(participant, -2, -2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WebSocketHandler::setPosition(T &tele, const WlsParticipant *participant)
|
||||
{
|
||||
tele.header.equipmentNumber = participant->equipmentNumber();
|
||||
tele.header.timeStamp = participant->positionTimestamp();
|
||||
|
||||
const WlsParticipant::RelativePosition &pos = participant->position();
|
||||
const QPointF lonLat(m_basePoint.geographic(pos.x, pos.y));
|
||||
tele.geoPosition.lon = lonLat.x();
|
||||
tele.geoPosition.lat = lonLat.y();
|
||||
tele.geoPosition.z = pos.z;
|
||||
|
||||
const int moutObjectId =
|
||||
m_ismHandler->buildingIdFromExternalWlsId(pos.wlsBuildingId);
|
||||
const int moutRoomId =
|
||||
m_ismHandler->roomIdFromRoomNumber(moutObjectId, pos.wlsRoomNumber);
|
||||
//tele.opsPosition.moutAreaId = pos.wlsAreaId; // needed?
|
||||
tele.opsPosition.moutObjectId = moutObjectId;
|
||||
tele.opsPosition.moutRoomId = moutRoomId;
|
||||
}
|
||||
|
||||
void WebSocketHandler::sendPosition(const WlsParticipant *participant)
|
||||
{
|
||||
INFO_LOG(m_logger) << "sendPosition equipmentNumber=" << participant->equipmentNumber();
|
||||
if (!participant || !participant->positionIsValid() || participant->equipmentNumber() == 0)
|
||||
{
|
||||
INFO_LOG(m_logger) << "not send - participant is not yet fully initialized";
|
||||
return;
|
||||
}
|
||||
|
||||
if (participant->isGpsMode())
|
||||
{
|
||||
INFO_LOG(m_logger) << "not send - participant is in gps mode";
|
||||
return;
|
||||
}
|
||||
TrainingAreaBase::TetraIcd::PositionReport posValue;
|
||||
setPosition(posValue, participant);
|
||||
sendTelegram(&posValue, participant->exerciseId());
|
||||
}
|
||||
|
||||
void WebSocketHandler::forceSendPosition(const WlsParticipant *participant,
|
||||
int moutObjectId, int moutRoomId)
|
||||
{
|
||||
// Convert Telegram to JSON-Object
|
||||
TrainingAreaBase::TetraIcd::PositionReport posValue;
|
||||
setPosition(posValue, participant);
|
||||
posValue.opsPosition.moutObjectId = moutObjectId;
|
||||
posValue.opsPosition.moutRoomId = moutRoomId;
|
||||
|
||||
sendTelegram(&posValue, participant->exerciseId());
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Websocket interface to the training area proxy
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-11
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
#include <QWebSocketServer>
|
||||
#include <memory>
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class IsmHandler;
|
||||
class WebSocketWrapper;
|
||||
class WlsHandler;
|
||||
class WlsParticipant;
|
||||
class WlsParticipantHandler;
|
||||
|
||||
class WebSocketHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WebSocketHandler(QObject *parent = nullptr);
|
||||
~WebSocketHandler();
|
||||
|
||||
bool init(std::shared_ptr<WlsHandler> wlsHandler,
|
||||
std::shared_ptr<WlsParticipantHandler> participantHandler,
|
||||
std::shared_ptr<IsmHandler> ismHandler,
|
||||
quint16 listenPort);
|
||||
|
||||
template <typename T>
|
||||
bool sendTelegram(const T *data, int exerciseId)
|
||||
{
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
sendTelegramInternal(data,
|
||||
TrainingAreaBase::TetraIcd::Constraints<T>::type(),
|
||||
exerciseId);
|
||||
|
||||
return true;
|
||||
}
|
||||
bool sendWlsStateReport(int exerciseId = 0);
|
||||
|
||||
public slots:
|
||||
void sendBinarySfmData(const WlsParticipant *participant);
|
||||
void sendDataCommunicationState(const WlsParticipant *participant);
|
||||
void sendGpsPositionUsage(const WlsParticipant *participant);
|
||||
void sendPosition(const WlsParticipant *participant);
|
||||
void forceSendPosition(const WlsParticipant *participant,
|
||||
int moutObjectId, int moutRoomId);
|
||||
|
||||
private slots:
|
||||
void onNewConnection();
|
||||
void onTextMessageReceived(const QString &baJson);
|
||||
|
||||
signals:
|
||||
void signalBroadcastMessage(const QString &msg, int exerciseId);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void setPosition(T &tele, const WlsParticipant *participant);
|
||||
void sendTelegramInternal(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *data,
|
||||
const QString &type, int exerciseId);
|
||||
void sendParticipantConnectionStates(int exerciseId);
|
||||
|
||||
QWebSocketServer *m_webSocketServer;
|
||||
std::shared_ptr<WlsHandler> m_wlsHandler;
|
||||
std::shared_ptr<IsmHandler> m_ismHandler;
|
||||
std::shared_ptr<WlsParticipantHandler> m_participantHandler;
|
||||
|
||||
LtsCoordTrans::UtmCoordinate m_basePoint;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Export definitions
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Mike Wille, SLSE, 2017-01-13
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifndef WLS_SFM_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define WLS_SFM_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_WLS_SFM_LIB)
|
||||
# define WLS_SFM_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define WLS_SFM_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
+527
@@ -0,0 +1,527 @@
|
||||
/**
|
||||
* Convert from/to WLS protocol / handler incoming WLS connection
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
|
||||
#include "wls_handler.h"
|
||||
|
||||
#include "main_handler.h"
|
||||
#include "settings.h"
|
||||
#include "wls_sfm_message_converter.h"
|
||||
#include "web_socket_handler.h"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QDateTime>
|
||||
#include <web_socket_handler.h>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
static constexpr unsigned char s_startMark = 0x7F;
|
||||
static constexpr unsigned char s_endMark = 0x8F;
|
||||
static inline QByteArray escapeData(const QByteArray &bufferData)
|
||||
{
|
||||
QByteArray ret;
|
||||
ret.reserve(bufferData.size() * 6 / 5);
|
||||
for (const unsigned char c : bufferData)
|
||||
{
|
||||
if (c >= 0x20 && c < 0x7f)
|
||||
ret += c;
|
||||
else
|
||||
ret += "\\x" + QByteArray::number(static_cast<unsigned char>(c), 16);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
WlsHandler::WlsHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_logger(getLogger("WlsHandler"))
|
||||
{
|
||||
if (Settings::wlsMgrsBasePoint().size() <= 15)
|
||||
WARNING_LOG(m_logger) << "WLS basepoint is not defined precise enough: " << Settings::wlsMgrsBasePoint();
|
||||
}
|
||||
|
||||
WlsHandler::~WlsHandler()
|
||||
{
|
||||
delete m_tcpServer;
|
||||
}
|
||||
|
||||
bool WlsHandler::init(WlsParticipantHandler *participantHandler,
|
||||
WebSocketHandler *wsHandler,
|
||||
quint16 listenPort)
|
||||
{
|
||||
m_participantHandler = participantHandler;
|
||||
m_wsHandler = wsHandler;
|
||||
INFO_LOG(m_logger) << "WlsHandler::init";
|
||||
delete m_tcpServer;
|
||||
m_tcpServer = new QTcpServer(this);
|
||||
connect(m_tcpServer, &QTcpServer::newConnection,
|
||||
this, &WlsHandler::onIncomingConnection);
|
||||
if (!m_tcpServer->listen(QHostAddress::Any, listenPort))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Error listening on port " << listenPort;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_wlsZOffset = Settings::wlsZOffset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WlsHandler::sendWlsMessage(const WlsParticipant *p, const SfmMessage_tx &msg)
|
||||
{
|
||||
return sendWlsMessage(p->tagId(), msg);
|
||||
}
|
||||
|
||||
bool WlsHandler::sendWlsMessage(const QByteArray &tagId, const SfmMessage_tx &msg)
|
||||
{
|
||||
return sendWlsMessage(tagId, WlsTelegramId::PayloadHostToMobile, SfmMessageConverter::generateTxMessage(msg));
|
||||
}
|
||||
|
||||
bool WlsHandler::sendWlsMessage(const QByteArray &tagId, WlsTelegramId telId, const QByteArray &payload)
|
||||
{
|
||||
if (!isConnected())
|
||||
return false;
|
||||
|
||||
if (telId != WlsTelegramId::PayloadHostToMobile)
|
||||
return false;
|
||||
|
||||
auto itSocket = m_tcpSocketByTagId.constFind(tagId);
|
||||
if (itSocket == m_tcpSocketByTagId.constEnd())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Can't find a TCP-socket for tag id: " << tagId;
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned int fragmentParts = ((payload.size() + s_maxLenTransparentData - 1) / s_maxLenTransparentData);
|
||||
const QDateTime dt = QDateTime::currentDateTimeUtc();
|
||||
QByteArray data;
|
||||
// start/end mark, 3*';', telId (can be 2 digits), timestring, \0
|
||||
data.reserve(2 + 3 + 2 + 23 + tagId.size() + s_maxLenTransparentData + 1);
|
||||
|
||||
for (unsigned int i = 0; i < fragmentParts; i++)
|
||||
{
|
||||
data.clear();
|
||||
|
||||
data.append(s_startMark); // Startkennung
|
||||
data.append(QByteArray::number(static_cast<int>(telId)));
|
||||
data.append(';'); // Trennzeichen
|
||||
data.append(dt.toString(QStringLiteral("yyyy-MM-dd hh:mm:ss.zzz")).toLatin1()); // Aktuelle Zeit
|
||||
data.append(';'); // Trennzeichen
|
||||
data.append(tagId); // Tag-ID
|
||||
data.append(';'); // Trennzeichen
|
||||
data.append(payload.mid(i * s_maxLenTransparentData, s_maxLenTransparentData));
|
||||
data.append(s_endMark); // Endekennung
|
||||
|
||||
(*itSocket)->write(data);
|
||||
DEBUG_LOG(m_logger) << "Sending telegram to wls: " << escapeData(data);
|
||||
}
|
||||
(*itSocket)->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WlsHandler::onIncomingConnection()
|
||||
{
|
||||
INFO_LOG(m_logger) << "WlsHandler::onIncomingConnection";
|
||||
QTcpSocket *tcpSocket = m_tcpServer->nextPendingConnection();
|
||||
if (!tcpSocket)
|
||||
return;
|
||||
m_tcpSockets[tcpSocket].clear();
|
||||
|
||||
connect(tcpSocket, &QTcpSocket::readyRead,
|
||||
this, &WlsHandler::onIncomingData);
|
||||
connect(tcpSocket, &QTcpSocket::disconnected,
|
||||
this, &WlsHandler::onSocketDisconnected);
|
||||
|
||||
// send wls connection state to all web sockets
|
||||
TrainingAreaBase::TetraIcd::ProxyConnectionState connState;
|
||||
connState.connectedToSystem = true;
|
||||
m_wsHandler->sendTelegram(&connState, 0);
|
||||
}
|
||||
|
||||
void WlsHandler::onIncomingData()
|
||||
{
|
||||
|
||||
QTcpSocket *s = qobject_cast<QTcpSocket *>(sender());
|
||||
if (!s)
|
||||
return;
|
||||
auto &tcpBuffer = m_tcpSockets[s];
|
||||
const QByteArray bufferData = tcpBuffer + s->readAll();
|
||||
TRACE_LOG(m_logger) << "WlsHandler::onIncomingData bufferData='" << escapeData(bufferData) << "'";
|
||||
// returns the unparsed part of the buffer
|
||||
tcpBuffer = parseBuffer(bufferData, s);
|
||||
|
||||
}
|
||||
|
||||
void WlsHandler::onSocketDisconnected()
|
||||
{
|
||||
QTcpSocket *s = qobject_cast<QTcpSocket *>(sender());
|
||||
if (!s)
|
||||
return;
|
||||
INFO_LOG(m_logger) << "Socket disconnected";
|
||||
m_tcpSockets.remove(s);
|
||||
|
||||
// remove tag ID to socket mappings
|
||||
auto it = m_tcpSocketByTagId.begin();
|
||||
while (it != m_tcpSocketByTagId.end())
|
||||
if (it.value() == s)
|
||||
it = m_tcpSocketByTagId.erase(it);
|
||||
else
|
||||
it++;
|
||||
|
||||
s->deleteLater();
|
||||
|
||||
// send wls connection state to all web sockets
|
||||
TrainingAreaBase::TetraIcd::ProxyConnectionState connState;
|
||||
connState.connectedToSystem = !m_tcpSockets.isEmpty();
|
||||
m_wsHandler->sendTelegram(&connState, 0);
|
||||
}
|
||||
|
||||
QByteArray WlsHandler::parseBuffer(const QByteArray &buffer, QTcpSocket *socket)
|
||||
{
|
||||
int iEndPos = 0;
|
||||
int iCurReadPos = 0;
|
||||
while (findNextSyncStartByte(buffer, iCurReadPos))
|
||||
{
|
||||
const int iStartPos = iCurReadPos;
|
||||
if (!findNextSyncEndByte(buffer, iCurReadPos))
|
||||
break;
|
||||
iEndPos = iCurReadPos;
|
||||
parseWlsTelegram(buffer.mid(iStartPos + 1, iEndPos - iStartPos - 1), socket);
|
||||
}
|
||||
return buffer.mid(iEndPos);
|
||||
}
|
||||
|
||||
void WlsHandler::parseWlsTelegram(const QByteArray &baTele, QTcpSocket *socket)
|
||||
{
|
||||
const QList<QByteArray> parts = baTele.split(';');
|
||||
if (parts.size() < 3)
|
||||
return;
|
||||
|
||||
// insert/update socket for tagId
|
||||
m_tcpSocketByTagId.insert(parts.at(2), socket);
|
||||
|
||||
const WlsTelegramId telegramId = static_cast<WlsTelegramId>(parts.at(0).toInt());
|
||||
const QDateTime timestamp = QDateTime::fromString(QString::fromUtf8(parts.at(1)), QStringLiteral("yyyy-MM-dd hh:mm:ss.zzz"));
|
||||
if (telegramId != WlsTelegramId::PlayerPosition)
|
||||
DEBUG_LOG(m_logger) << "Handling " << telegramId
|
||||
<< ", data: '" << escapeData(baTele) << "'";
|
||||
bool bOk = true;
|
||||
switch (telegramId)
|
||||
{
|
||||
case WlsTelegramId::VersionInformation:
|
||||
bOk = handleVersionsinformation(parts);
|
||||
break;
|
||||
case WlsTelegramId::PlayerPosition:
|
||||
bOk = handlePlayerPosition(parts, timestamp);
|
||||
break;
|
||||
case WlsTelegramId::PlayerState:
|
||||
bOk = handlePlayerState(parts);
|
||||
break;
|
||||
case WlsTelegramId::PayloadMobileToHost:
|
||||
bOk = handleDataPayloadMobileToHost(parts);
|
||||
break;
|
||||
case WlsTelegramId::PayloadHostToMobile:
|
||||
WARNING_LOG(m_logger) << "Protocol error - got a message which should not be sent from Wls (PayloadHostToMobile)";
|
||||
break;
|
||||
}
|
||||
if (!bOk)
|
||||
WARNING_LOG(m_logger) << "Error handling telegram: '" << baTele << "'";
|
||||
}
|
||||
|
||||
bool WlsHandler::findNextSyncStartByte(const QByteArray &buffer, int &iCurReadPos)
|
||||
{
|
||||
// Suchen nach s_startMark
|
||||
while (iCurReadPos < buffer.size())
|
||||
{
|
||||
const unsigned char data = buffer.at(iCurReadPos);
|
||||
if (data == s_startMark)
|
||||
return true;
|
||||
++iCurReadPos;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WlsHandler::findNextSyncEndByte(const QByteArray &buffer, int &iCurReadPos)
|
||||
{
|
||||
// Suchen nach s_endMark
|
||||
while (iCurReadPos < buffer.size())
|
||||
{
|
||||
const unsigned char data = buffer.at(iCurReadPos);
|
||||
if (data == s_endMark)
|
||||
return true;
|
||||
++iCurReadPos;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WlsHandler::handleVersionsinformation(const QList<QByteArray> &data)
|
||||
{
|
||||
if (data.size() < 4)
|
||||
return false;
|
||||
DEBUG_LOG(m_logger) << "TX(WLS): Version = " << data.at(3);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WlsHandler::handlePlayerPosition(const QList<QByteArray> &data, const QDateTime ×tamp)
|
||||
{
|
||||
if (data.size() < 6)
|
||||
return false;
|
||||
|
||||
WlsParticipant *p = participantFromTagId(data.at(2));
|
||||
|
||||
WlsParticipant::RelativePosition pos;
|
||||
int iPos = 3;
|
||||
if (!parseDoubleCm(data.at(iPos++), pos.x))
|
||||
return false;
|
||||
if (!parseDoubleCm(data.at(iPos++), pos.y))
|
||||
return false;
|
||||
if (!parseDoubleCm(data.at(iPos++), pos.z))
|
||||
return false;
|
||||
if ((data.size() > iPos) && !parseInteger(data.at(iPos++), pos.wlsAreaId))
|
||||
return false;
|
||||
if ((data.size() > iPos) && !parseInteger(data.at(iPos++), pos.wlsBuildingId))
|
||||
return false;
|
||||
if ((data.size() > iPos) && !parseInteger(data.at(iPos++), pos.wlsRoomNumber))
|
||||
return false;
|
||||
if ((data.size() > iPos) && !parseInteger(data.at(iPos++), pos.playerOrientation))
|
||||
return false;
|
||||
if ((data.size() > iPos) && !parseInteger(data.at(iPos++), pos.weaponOrientation))
|
||||
return false;
|
||||
|
||||
pos.z += m_wlsZOffset;
|
||||
p->setPosition(pos, timestamp.toMSecsSinceEpoch());
|
||||
|
||||
INFO_LOG(m_logger) << p->getPlayerIdStr() << ": handlePlayerPosition"
|
||||
<< " x=" << pos.x << ", y=" << pos.y << ", z=" << pos.z
|
||||
<< ", wlsAreaId=" << pos.wlsAreaId
|
||||
<< ", wlsBuildingId=" << pos.wlsBuildingId
|
||||
<< ", wlsRoomNumber=" << pos.wlsRoomNumber
|
||||
<< ", zone=" << p->wlsZone()
|
||||
<< ", gpsMode=" << p->gpsMode()
|
||||
<< ", dataCommunicationPossible: " << p->dataCommunication()
|
||||
<< ", " << timestamp.toString();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WlsHandler::handlePlayerState(const QList<QByteArray> &data)
|
||||
{
|
||||
if (data.size() < 6)
|
||||
return false;
|
||||
|
||||
WlsParticipant *p = participantFromTagId(data.at(2));
|
||||
|
||||
int timeout;
|
||||
if (!parseInteger(data.at(3), timeout))
|
||||
return false;
|
||||
|
||||
int tacticalState;
|
||||
if (!parseInteger(data.at(4), tacticalState))
|
||||
return false;
|
||||
|
||||
int voltage;
|
||||
if (!parseInteger(data.at(5), voltage))
|
||||
return false;
|
||||
|
||||
p->voltage = voltage / 10.; // 1/10V
|
||||
|
||||
INFO_LOG(m_logger) << p->getPlayerIdStr() << ": handlePlayerState"
|
||||
<< " timeout=" << timeout
|
||||
<< " ,tacticalState=" << tacticalState
|
||||
<< " ,voltage=" << p->voltage;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WlsHandler::checkPayloadIsHex(const QByteArray &payload) const
|
||||
{
|
||||
for (const char c : payload)
|
||||
{
|
||||
if ((c < '0' || c > '9') &&
|
||||
(c < 'a' || c > 'f') &&
|
||||
(c < 'A' || c > 'F'))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Payload data is not hex coded ...";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WlsHandler::handleDataPayloadMobileToHost(const QList<QByteArray> &data)
|
||||
{
|
||||
if (data.size() < 4)
|
||||
return false;
|
||||
|
||||
const QByteArray &payload = data.at(3);
|
||||
const QByteArray &tagId = data.at(2);
|
||||
if (!checkPayloadIsHex(payload))
|
||||
return false;
|
||||
|
||||
QSharedPointer<SfmMessage_rx> msg = SfmMessageConverter::generateRxMessage(payload);
|
||||
if (!msg)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Can not convert payload to SfmMessage ... ";
|
||||
return false;
|
||||
}
|
||||
|
||||
WlsParticipant *p = participantFromTagId(tagId, msg->getId() != SfmMessage::MsgId::EquipmentId);
|
||||
p->setLastSeenWlsTelegramTime(QDateTime::currentMSecsSinceEpoch());
|
||||
|
||||
switch (msg->getId())
|
||||
{
|
||||
case SfmMessage::MsgId::EquipmentId:
|
||||
handlePayload(p, static_cast<SfmMsgEquipmentId *>(msg.data()));
|
||||
break;
|
||||
case SfmMessage::MsgId::RequestGpsControl:
|
||||
handlePayload(p, static_cast<SfmMsgRequestGpsControl_rx *>(msg.data()));
|
||||
break;
|
||||
case SfmMessage::MsgId::SfmRestartNotification:
|
||||
handlePayload(p, static_cast<SfmMsgSfmRestart_rx *>(msg.data()));
|
||||
break;
|
||||
case SfmMessage::MsgId::Acknowledge:
|
||||
handlePayload(p, static_cast<Acknowledge_rx *>(msg.data()));
|
||||
break;
|
||||
case SfmMessage::MsgId::TetraMessage:
|
||||
handlePayload(p, static_cast<SfmMsgTetra_rx *>(msg.data()));
|
||||
break;
|
||||
case SfmMessage::MsgId::LicenseState:
|
||||
case SfmMessage::MsgId::TacticalState:
|
||||
default:
|
||||
WARNING_LOG(m_logger) << "RX(WLS): MessageID(" << static_cast<int>(msg->getId()) << ")";
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void WlsHandler::handlePayload(WlsParticipant *participant, const SfmMsgEquipmentId *sfmTele)
|
||||
{
|
||||
const int equipmentNumber = sfmTele->getEquipmentNumber();
|
||||
INFO_LOG(m_logger) << participant->getPlayerIdStr() << ": handlePayload"
|
||||
<< " - new equipmentNumber = " << equipmentNumber;
|
||||
if (participant->equipmentNumber() == 0 ||
|
||||
participant->equipmentNumber() != equipmentNumber)
|
||||
{
|
||||
m_participantHandler->setEquipmentNumber(participant, equipmentNumber);
|
||||
}
|
||||
}
|
||||
|
||||
void WlsHandler::handlePayload(WlsParticipant *participant, const SfmMsgRequestGpsControl_rx *sfmTele)
|
||||
{
|
||||
Q_UNUSED(sfmTele);
|
||||
sendGpsMode(participant);
|
||||
}
|
||||
|
||||
void WlsHandler::handlePayload(WlsParticipant *participant, const SfmMsgSfmRestart_rx *sfmTele)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Restart notification ... " << participant->tagId();
|
||||
Q_UNUSED(sfmTele)
|
||||
|
||||
participant->setWlsZone(-1);
|
||||
// TODO: noch EXCON informieren?
|
||||
}
|
||||
|
||||
void WlsHandler::handlePayload(WlsParticipant *participant, const Acknowledge_rx *sfmTele)
|
||||
{
|
||||
participant->handleWlsAck(sfmTele);
|
||||
}
|
||||
|
||||
void WlsHandler::handlePayload(WlsParticipant *participant, const SfmMsgTetra_rx *sfmTele)
|
||||
{
|
||||
Acknowledge_tx msg;
|
||||
if (!participant->handleSfmRxData(sfmTele))
|
||||
msg.setFragmentError(participant->nextSfmRxDataFragmentCounter());
|
||||
msg.setRecvCounter(sfmTele->sendCounter());
|
||||
sendWlsMessage(participant->tagId(), msg);
|
||||
}
|
||||
|
||||
void WlsHandler::sendGpsMode(WlsParticipant *participant)
|
||||
{
|
||||
if (!participant)
|
||||
return;
|
||||
SfmMsgGpsControl_tx msg;
|
||||
msg.setGpsOn(participant->isGpsMode());
|
||||
INFO_LOG(m_logger) << participant->getPlayerIdStr() << ": sendGpsMode"
|
||||
<< " GpsMode = " << participant->isGpsMode();
|
||||
sendWlsMessage(participant->tagId(), msg);
|
||||
}
|
||||
|
||||
bool WlsHandler::parseDoubleCm(const QByteArray &data, double &num) const
|
||||
{
|
||||
bool bOk;
|
||||
num = data.toDouble(&bOk);
|
||||
if (!bOk)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Error parsing floating point number '" << data << "'";
|
||||
return false;
|
||||
}
|
||||
num /= 100; // cm -> m
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WlsHandler::parseInteger(const QByteArray &data, int &num) const
|
||||
{
|
||||
bool bOk;
|
||||
num = data.toInt(&bOk);
|
||||
if (!bOk)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Error parsing number '" << data << "'";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
WlsParticipant *WlsHandler::participantFromTagId(const QByteArray &tagId, bool bAskForEqIdIfNew)
|
||||
{
|
||||
WlsParticipant *p = m_participantHandler->participantByTagId(tagId);
|
||||
bool bRequestEquipmentNumber = false;
|
||||
const qint64 tNow = QDateTime::currentMSecsSinceEpoch();
|
||||
if (!p)
|
||||
{
|
||||
p = m_participantHandler->createNewParticipant(tagId);
|
||||
bRequestEquipmentNumber = bAskForEqIdIfNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wenn noch keine EquipmentNumber gekommen ist oder die letzte Anfrage dazu älter als 10 Sekunden ist
|
||||
if (p->equipmentNumber() <= 0 && p->lastEquipmentNumberRequest < tNow - 10 * 1000)
|
||||
bRequestEquipmentNumber = true;
|
||||
}
|
||||
if (bRequestEquipmentNumber)
|
||||
{
|
||||
// TODO: wirklich nur wenn der Teilnehmer noch nicht vorhanden ist?
|
||||
// was, wenn wir das Antworttelegramm nicht bekommen? Dann wird aktuell nie eine AusstgNr hinzugefügt
|
||||
// -> Manfred Gronak fragen
|
||||
SfmMsgRequestEquipmentId_tx getEqId;
|
||||
INFO_LOG(m_logger) << "Requesting Equipment number for tag " << tagId;
|
||||
sendWlsMessage(tagId, getEqId);
|
||||
p->lastEquipmentNumberRequest = tNow;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, WlsHandler::WlsTelegramId ti)
|
||||
{
|
||||
switch (ti)
|
||||
{
|
||||
case WlsHandler::WlsTelegramId::VersionInformation:
|
||||
return stream << "WlsTelegramId::VersionInformation";
|
||||
case WlsHandler::WlsTelegramId::PlayerPosition:
|
||||
return stream << "WlsTelegramId::PlayerPosition";
|
||||
case WlsHandler::WlsTelegramId::PlayerState:
|
||||
return stream << "WlsTelegramId::PlayerState";
|
||||
case WlsHandler::WlsTelegramId::PayloadMobileToHost:
|
||||
return stream << "WlsTelegramId::PayloadMobileToHost";
|
||||
case WlsHandler::WlsTelegramId::PayloadHostToMobile:
|
||||
return stream << "WlsTelegramId::PayloadHostToMobile";
|
||||
}
|
||||
return stream << "Unknown WlsTelegramId (" << std::to_string(int(ti)) << ")";
|
||||
}
|
||||
} // namespace WirelessLocationSystem
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Convert from/to WLS protocol / handler incoming WLS connection
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QTimer>
|
||||
#include <memory>
|
||||
#include "logger.h"
|
||||
#include "wls_participant_handler.h"
|
||||
#include "web_socket_handler.h"
|
||||
#include "wls_sfm_message.h"
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class MainHandler;
|
||||
class WlsHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
enum class WlsTelegramId
|
||||
{
|
||||
VersionInformation = 0,
|
||||
PlayerPosition = 1,
|
||||
PlayerState = 2,
|
||||
PayloadMobileToHost = 3,
|
||||
PayloadHostToMobile = 4
|
||||
};
|
||||
friend std::ostream &operator<<(std::ostream &stream, WlsHandler::WlsTelegramId ti);
|
||||
|
||||
public:
|
||||
static constexpr int s_maxLenTransparentData = 80;
|
||||
|
||||
explicit WlsHandler(QObject *parent = nullptr);
|
||||
~WlsHandler();
|
||||
|
||||
bool init(WlsParticipantHandler *participantHandler,
|
||||
WebSocketHandler *wsHandler,
|
||||
quint16 listenPort);
|
||||
|
||||
bool isConnected() const
|
||||
{
|
||||
return !m_tcpSockets.isEmpty();
|
||||
}
|
||||
|
||||
bool sendWlsMessage(const WlsParticipant *p, const SfmMessage_tx &msg);
|
||||
bool sendWlsMessage(const QByteArray &tagId, const SfmMessage_tx &msg);
|
||||
bool sendWlsMessage(const QByteArray &tagId, WlsTelegramId telId, const QByteArray &payload);
|
||||
|
||||
public Q_SLOTS:
|
||||
void sendGpsMode(WlsParticipant *participant);
|
||||
private Q_SLOTS:
|
||||
void onIncomingConnection();
|
||||
void onIncomingData();
|
||||
void onSocketDisconnected();
|
||||
private:
|
||||
QByteArray parseBuffer(const QByteArray &buffer, QTcpSocket *socket);
|
||||
void parseWlsTelegram(const QByteArray &baTele, QTcpSocket *socket);
|
||||
static bool findNextSyncStartByte(const QByteArray &buffer, int &iCurReadPos);
|
||||
static bool findNextSyncEndByte(const QByteArray &buffer, int &iCurReadPos);
|
||||
bool checkPayloadIsHex(const QByteArray &payload) const;
|
||||
|
||||
bool handleVersionsinformation(const QList<QByteArray> &data);
|
||||
bool handlePlayerPosition(const QList<QByteArray> &data, const QDateTime ×tamp);
|
||||
bool handlePlayerState(const QList<QByteArray> &data);
|
||||
bool handleDataPayloadMobileToHost(const QList<QByteArray> &data);
|
||||
void handlePayload(WlsParticipant *participant, const SfmMsgEquipmentId *sfmTele);
|
||||
void handlePayload(WlsParticipant *participant, const SfmMsgRequestGpsControl_rx *sfmTele);
|
||||
void handlePayload(WlsParticipant *participant, const SfmMsgSfmRestart_rx *sfmTele);
|
||||
void handlePayload(WlsParticipant *participant, const Acknowledge_rx *sfmTele);
|
||||
void handlePayload(WlsParticipant *participant, const SfmMsgTetra_rx *sfmTele);
|
||||
|
||||
bool parseDoubleCm(const QByteArray &data, double &num) const;
|
||||
bool parseInteger(const QByteArray &data, int &num) const;
|
||||
WlsParticipant *participantFromTagId(const QByteArray &tagId, bool bAskForEqIdIfNew = true);
|
||||
|
||||
private:
|
||||
QTcpServer *m_tcpServer = nullptr;
|
||||
WlsParticipantHandler *m_participantHandler = nullptr;
|
||||
WebSocketHandler *m_wsHandler = nullptr;
|
||||
// one buffer per socket
|
||||
QHash<QTcpSocket *, QByteArray> m_tcpSockets;
|
||||
QHash<QByteArray, QTcpSocket *> m_tcpSocketByTagId;
|
||||
|
||||
double m_wlsZOffset = 0;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Small test program to connect to the wls websocket
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#include "wls_json_client_test.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
WlsJsonclientTest::WlsJsonclientTest(const QString &hostname, unsigned short port)
|
||||
: m_socket(new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this))
|
||||
, m_hostname(hostname)
|
||||
, m_port(port)
|
||||
{
|
||||
connect(m_socket, &QWebSocket::connected, this, &WlsJsonclientTest::onConnected);
|
||||
connect(m_socket, &QWebSocket::disconnected, this, &WlsJsonclientTest::onClosed);
|
||||
connect(m_socket, &QWebSocket::textMessageReceived, this, &WlsJsonclientTest::onTextMessageReceived);
|
||||
QTimer::singleShot(0, this, &WlsJsonclientTest::run);
|
||||
}
|
||||
|
||||
void WlsJsonclientTest::run()
|
||||
{
|
||||
const QUrl url(QStringLiteral("ws://%1:%2/").arg(m_hostname).arg(m_port));
|
||||
qDebug("Trying to connect to %s", qPrintable(url.toString()));
|
||||
m_socket->open(url);
|
||||
}
|
||||
|
||||
void WlsJsonclientTest::onConnected()
|
||||
{
|
||||
qDebug("Connected to web socket server");
|
||||
}
|
||||
|
||||
void WlsJsonclientTest::onClosed()
|
||||
{
|
||||
qWarning("Socket closed - quit app");
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
void WlsJsonclientTest::onTextMessageReceived(const QString &msg)
|
||||
{
|
||||
qDebug("Received text msg: '%s'", qPrintable(msg));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
unsigned short port = 12346;
|
||||
QString hostname = QStringLiteral("localhost");
|
||||
if (app.arguments().size() > 1)
|
||||
hostname = app.arguments().at(1);
|
||||
if (app.arguments().size() > 2)
|
||||
port = app.arguments().at(2).toUShort();
|
||||
|
||||
WlsJsonclientTest *w = new WlsJsonclientTest(hostname, port);
|
||||
int ret = app.exec();
|
||||
delete w;
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Small test program to connect to the wls websocket
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QWebSocket>
|
||||
|
||||
class WlsJsonclientTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WlsJsonclientTest(const QString &hostname, unsigned short port);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void run();
|
||||
void onConnected();
|
||||
void onClosed();
|
||||
void onTextMessageReceived(const QString &msg);
|
||||
private:
|
||||
QWebSocket *m_socket;
|
||||
QString m_hostname;
|
||||
unsigned short m_port;
|
||||
};
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
/**
|
||||
* A single Participant
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#include "wls_participant.h"
|
||||
#include "wls_sfm_message.h"
|
||||
#include "wls_handler.h"
|
||||
#include "settings.h"
|
||||
#include <QDateTime>
|
||||
#include <LtsUtilsCore/dds_vector.h>
|
||||
#include <LtsUtilsCore/sfm_bytemessage_converter.h>
|
||||
|
||||
static constexpr qint64 s_dataCommunicationAliveTimeout = 5 * 1000;
|
||||
static constexpr int s_maxWlsRetryCount = 3;
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
WlsParticipant::WlsParticipant(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_wlsTeleTimeoutTimer(new QTimer(this))
|
||||
, m_logger(getLogger("WlsParticipant"))
|
||||
{
|
||||
m_wlsTeleTimeoutTimer->setSingleShot(true);
|
||||
m_wlsTeleTimeoutTimer->setTimerType(Qt::VeryCoarseTimer);
|
||||
m_wlsTeleTimeoutTimer->setInterval(s_dataCommunicationAliveTimeout);
|
||||
connect(m_wlsTeleTimeoutTimer, &QTimer::timeout, this, &WlsParticipant::onWlsTeleTimeout);
|
||||
}
|
||||
|
||||
QByteArray WlsParticipant::getPlayerIdStr() const
|
||||
{
|
||||
QByteArray ret;
|
||||
if (m_equipmentNumber > 0)
|
||||
ret = QByteArray::number(m_equipmentNumber);
|
||||
else
|
||||
ret = "unknown";
|
||||
if (m_tagId.isEmpty())
|
||||
ret += "(unknown tag id)";
|
||||
else
|
||||
ret += "(" + m_tagId + ")";
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WlsParticipant::setEquipmentNumber(int newEquipmentNumber)
|
||||
{
|
||||
if (m_equipmentNumber == newEquipmentNumber)
|
||||
return;
|
||||
m_equipmentNumber = newEquipmentNumber;
|
||||
|
||||
emit dataCommunicationStateChanged(this);
|
||||
emit positionChanged(this);
|
||||
}
|
||||
|
||||
void WlsParticipant::setPosition(const RelativePosition &newPosition,
|
||||
qint64 tsCurrentPos)
|
||||
{
|
||||
if (tsCurrentPos == m_lastSeenPosition ||
|
||||
(((tsCurrentPos - m_lastSeenPosition) <= Settings::deadPosTimeout()) && comparePos(m_relativePosition, newPosition)))// same timestamp or same pos for 5 sec
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Check for pos Update";
|
||||
DEBUG_LOG(m_logger) << "Is Outdated= " <<((tsCurrentPos - m_lastSeenPosition) <= Settings::deadPosTimeout());
|
||||
|
||||
if (((tsCurrentPos - m_lastSeenPosition) <= Settings::deadPosTimeout()) && comparePos(m_relativePosition, newPosition))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Same Position or not older than 5000msecs " << tsCurrentPos - m_lastSeenPosition;
|
||||
return;
|
||||
}
|
||||
const qint64 tsNow = QDateTime::currentMSecsSinceEpoch();
|
||||
if (tsCurrentPos == m_lastSeenPosition && m_lastValidPosition + 9 * 60 * 1000 > tsNow) // younger than 9 min -> ignore //should never be true as agilion always send with new timestamp
|
||||
return; // position did not change
|
||||
|
||||
// did not move in the last 9min, send a new position report to excon
|
||||
m_lastValidPosition = tsNow;
|
||||
if (isDataCommunicationPossible())
|
||||
emit positionChanged(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int newWlsZone = WlsParticipant::calcWlsZone(newPosition.wlsAreaId,
|
||||
newPosition.wlsBuildingId,
|
||||
newPosition.wlsRoomNumber);
|
||||
setWlsZone(newWlsZone);
|
||||
|
||||
// to create a new position report on restart / after 9 min timeout
|
||||
m_lastSeenPosition = tsCurrentPos;
|
||||
m_lastValidPosition = tsCurrentPos;
|
||||
|
||||
m_relativePosition = newPosition;
|
||||
m_relativePosition.isValid = true;
|
||||
|
||||
if (isDataCommunicationPossible())
|
||||
emit positionChanged(this);
|
||||
}
|
||||
|
||||
void WlsParticipant::checkGpsOnOffTimeoutTimer(qint64 tNow)
|
||||
{
|
||||
// full blown tetra mode -> nothing to do
|
||||
if (m_gpsMode == GpsMode::On ||
|
||||
m_dataCommunication == DataCommunication::Off)
|
||||
return;
|
||||
if (m_lastSeenDataCommunication > tNow - s_maxWlsRetryCount * s_dataCommunicationAliveTimeout)
|
||||
return;
|
||||
|
||||
DEBUG_LOG(m_logger) << getPlayerIdStr() << ": lastSeenDataCommunication: "
|
||||
<< m_lastSeenDataCommunication << ", now: "
|
||||
<< (tNow - s_maxWlsRetryCount * s_dataCommunicationAliveTimeout)
|
||||
<< " (" << tNow << "), "
|
||||
<< m_dataCommunication << ", "
|
||||
<< m_gpsMode;
|
||||
|
||||
// no alive from participant -> switch back to
|
||||
if (m_dataCommunication == DataCommunication::On)
|
||||
{
|
||||
m_dataCommunication = DataCommunication::Off;
|
||||
emit dataCommunicationStateChanged(this);
|
||||
}
|
||||
if (m_gpsMode == GpsMode::Off)
|
||||
{
|
||||
m_gpsMode = GpsMode::On;
|
||||
emit gpsOnOffChanged(this);
|
||||
}
|
||||
m_wlsZone = 0;
|
||||
}
|
||||
|
||||
void WlsParticipant::setLastSeenWlsTelegramTime(qint64 ts)
|
||||
{
|
||||
m_lastSeenDataCommunication = ts;
|
||||
// current only allowed when wlsZone > 3
|
||||
// m_relativePosition.dataCommunication = WlsParticipant::DataCommunication::On;
|
||||
}
|
||||
|
||||
void WlsParticipant::setWlsZone(int newWlsZone)
|
||||
{
|
||||
const auto oldGpsMode = m_gpsMode;
|
||||
const auto oldDataComm = m_dataCommunication;
|
||||
if (newWlsZone >= 0) // < 0 == restart
|
||||
{
|
||||
if (WlsParticipant::mustSendGpsOff(m_wlsZone, newWlsZone))
|
||||
m_gpsMode = GpsMode::Off;
|
||||
else if (WlsParticipant::mustSendGpsOn(m_wlsZone, newWlsZone))
|
||||
m_gpsMode = GpsMode::On;
|
||||
|
||||
m_dataCommunication = (newWlsZone > 3)
|
||||
? DataCommunication::On
|
||||
: DataCommunication::Off;
|
||||
INFO_LOG(m_logger) << getPlayerIdStr() << ": wlsZone old: " << m_wlsZone << ", new: " << newWlsZone
|
||||
<< ", new gps mode: " << m_gpsMode
|
||||
<< ", new data comm mode: " << m_dataCommunication
|
||||
<< ", " << this;
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(m_logger) << getPlayerIdStr() << ": Reset zone/data comunication status - sfm was restarted";
|
||||
m_gpsMode = GpsMode::On;
|
||||
m_dataCommunication = DataCommunication::Unknown;
|
||||
m_lastSeenPosition = -1;
|
||||
}
|
||||
m_wlsZone = newWlsZone;
|
||||
|
||||
if (oldDataComm != m_dataCommunication)
|
||||
{
|
||||
if (m_dataCommunication == DataCommunication::On)
|
||||
m_lastSeenDataCommunication = QDateTime::currentMSecsSinceEpoch();
|
||||
emit dataCommunicationStateChanged(this);
|
||||
}
|
||||
if (oldGpsMode != m_gpsMode)
|
||||
emit gpsOnOffChanged(this);
|
||||
}
|
||||
|
||||
bool WlsParticipant::handleSfmRxData(const SfmMsgTetra_rx *sfmTele)
|
||||
{
|
||||
if (!isRecvCounterCorrect(sfmTele->sendCounter()))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Got data telegram with counter " << int(sfmTele->sendCounter())
|
||||
<< " last one was " << int(m_lastRecvCounter) << " - ignoring";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bSfmPayloadReady = false;
|
||||
if (sfmTele->isFragmented())
|
||||
{
|
||||
if (m_sfmRxPayloadFragmentNumber + 1 != sfmTele->fragmentNumber())
|
||||
{
|
||||
WARNING_LOG(m_logger) << getPlayerIdStr() << ": Missing a fragment (waiting for "
|
||||
<< (m_sfmRxPayloadFragmentNumber + 1)
|
||||
<< ", got " << sfmTele->fragmentNumber() << ")";
|
||||
if (sfmTele->fragmentNumber() != 1)
|
||||
return false;
|
||||
// assume the last fragmented telegram did not came through, start over
|
||||
m_sfmRxPayloadFragmentNumber = 0;
|
||||
}
|
||||
if (m_sfmRxPayloadFragmentNumber == 0) // first fragment - clear buffer
|
||||
m_sfmRxPayloadData.clear();
|
||||
++m_sfmRxPayloadFragmentNumber;
|
||||
m_sfmRxPayloadData += sfmTele->payload();
|
||||
m_bSfmPayloadReady = (sfmTele->fragmentCount() == sfmTele->fragmentNumber());
|
||||
|
||||
if (m_bSfmPayloadReady) // last fragment - reset fragment counter
|
||||
m_sfmRxPayloadFragmentNumber = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not fragmented
|
||||
if (m_sfmRxPayloadFragmentNumber != 0)
|
||||
WARNING_LOG(m_logger) << getPlayerIdStr()
|
||||
<< ": Got unfragmented telegram but currently waiting for next fragment of a fragmented telegram";
|
||||
m_sfmRxPayloadFragmentNumber = 0; // assume the last fragmenation did not came through
|
||||
m_sfmRxPayloadData = sfmTele->payload();
|
||||
m_bSfmPayloadReady = true;
|
||||
}
|
||||
|
||||
if (m_bSfmPayloadReady)
|
||||
emit wlsBinarySfmDataComplete(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WlsParticipant::calcChunkCountAndLen(const QByteArray &data, int &chunkCount, int &chunkLen)
|
||||
{
|
||||
// hex as ascii -> / 2
|
||||
// STX
|
||||
// msgid (2bytes, current msgIds are all fine - no substitution needed),
|
||||
// msg length is in message, we assume it must be subsituted (using 0x02 for checks)
|
||||
// fragment byte is correctly calculated
|
||||
// checksum byte (2 bytes worst case)
|
||||
// ETX
|
||||
constexpr int maxBytesPerChunk = (WlsHandler::s_maxLenTransparentData / 2) - (1 + 2 + 2 + 1);
|
||||
|
||||
chunkLen = 0;
|
||||
chunkCount = 1;
|
||||
bool bChunkCountIsOk = true;
|
||||
QByteArray chunk;
|
||||
for (int i = chunkCount; i < 16; ++i) // max. 15 chunks
|
||||
{
|
||||
bChunkCountIsOk = true;
|
||||
chunkLen = (data.size() + i - 1) / i;
|
||||
|
||||
for (int j = 0; j < i; ++j)
|
||||
{
|
||||
const char fragByte = SfmMsgTetra_tx::fragmentByte(i, j + 1);
|
||||
chunk.clear();
|
||||
chunk += 0x02; // a send counter which needs to be substituted
|
||||
chunk += fragByte;
|
||||
chunk += data.mid(j * chunkLen, chunkLen);
|
||||
|
||||
const int payloadLen = LtsUtils::lenMaskedData(chunk);
|
||||
if (payloadLen > maxBytesPerChunk)
|
||||
{
|
||||
bChunkCountIsOk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bChunkCountIsOk)
|
||||
{
|
||||
chunkCount = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bChunkCountIsOk;
|
||||
}
|
||||
|
||||
void WlsParticipant::sendBinarySfmData(const std::vector<unsigned char> &data)
|
||||
{
|
||||
int chunkLen = 0;
|
||||
int chunkCount = 0;
|
||||
|
||||
const QByteArray ba = LtsUtils::toQByteArray(data);
|
||||
if (!calcChunkCountAndLen(ba, chunkCount, chunkLen))
|
||||
{
|
||||
WARNING_LOG(m_logger) << getPlayerIdStr()
|
||||
<< ": Internal error calculating chunk size for payload size " << data.size();
|
||||
return;
|
||||
}
|
||||
|
||||
WlsPayloadData payload;
|
||||
payload.fragmentCount = chunkCount;
|
||||
payload.fragmentNumber = 0;
|
||||
int iPos = 0;
|
||||
while (iPos < ba.size())
|
||||
{
|
||||
++payload.fragmentNumber;
|
||||
payload.payload = ba.mid(iPos, chunkLen);
|
||||
m_wlsQueue.enqueue(payload);
|
||||
iPos += chunkLen;
|
||||
}
|
||||
|
||||
QTimer::singleShot(0, this, &WlsParticipant::checkSendTele);
|
||||
}
|
||||
|
||||
void WlsParticipant::handleWlsAck(const Acknowledge_rx *ack)
|
||||
{
|
||||
// the ack recv counter must match our send counter
|
||||
if (ack->recvCounter() != m_nextSendCounter)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Got ack for telegram with counter " << int(ack->recvCounter())
|
||||
<< " but expected " << int(m_nextSendCounter) << " - ignoring";
|
||||
return;
|
||||
}
|
||||
if (ack->isOk())
|
||||
{
|
||||
if (m_wlsQueue.isEmpty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "got an ack but nothing was sent - something went terribly wrong - resetting send/recv counter";
|
||||
m_nextSendCounter = 0;
|
||||
m_lastRecvCounter = 0;
|
||||
return;
|
||||
}
|
||||
const WlsPayloadData &h = m_wlsQueue.head();
|
||||
if (h.fragmentCount > 1)
|
||||
{
|
||||
if (h.fragmentCount == h.fragmentNumber)
|
||||
m_currentFragmentedTeleQueue.clear(); // sent complete fragmented telegram - can throw away the segments
|
||||
else
|
||||
m_currentFragmentedTeleQueue.enqueue(h); // save for possible resend
|
||||
}
|
||||
m_wlsQueue.dequeue();
|
||||
m_wlsTeleTimeoutTimer->stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we sent a fragmented telegram then we've to start over from the beginning
|
||||
while (!m_currentFragmentedTeleQueue.isEmpty())
|
||||
m_wlsQueue.push_front(m_currentFragmentedTeleQueue.dequeue());
|
||||
}
|
||||
checkSendTele();
|
||||
}
|
||||
|
||||
void WlsParticipant::checkSendTele()
|
||||
{
|
||||
if (m_wlsTeleTimeoutTimer->isActive())
|
||||
return;
|
||||
sendNextWlsTelegram(false);
|
||||
}
|
||||
|
||||
void WlsParticipant::sendNextWlsTelegram(bool bIsResend)
|
||||
{
|
||||
if (m_wlsQueue.isEmpty())
|
||||
return;
|
||||
if (m_dataCommunication == WlsParticipant::DataCommunication::Off)
|
||||
{
|
||||
if (bIsResend)
|
||||
{
|
||||
// when we try to resend due to timeout but now we know that there is no
|
||||
// data communication anymore -> throw away
|
||||
m_wlsQueue.clear();
|
||||
return;
|
||||
}
|
||||
WARNING_LOG(m_logger) << getPlayerIdStr()
|
||||
<< ": Trying to send a wls telegram but no data communication possible";
|
||||
// should not happen so leave the assertion to find out the reasons for it
|
||||
Q_ASSERT(false);
|
||||
m_wlsQueue.clear();
|
||||
return;
|
||||
}
|
||||
const auto &tele = m_wlsQueue.head();
|
||||
SfmMsgTetra_tx tx(getNextSendCounter(bIsResend),
|
||||
tele.fragmentCount,
|
||||
tele.fragmentNumber,
|
||||
tele.payload);
|
||||
emit dataForWls(this, tx);
|
||||
if (!bIsResend)
|
||||
m_wlsTeleTimeoutCounter = 0;
|
||||
m_wlsTeleTimeoutTimer->start();
|
||||
}
|
||||
|
||||
void WlsParticipant::onWlsTeleTimeout()
|
||||
{
|
||||
++m_wlsTeleTimeoutCounter;
|
||||
DEBUG_LOG(m_logger) << getPlayerIdStr()
|
||||
<< ": Resend WLS connection to participant " << m_equipmentNumber
|
||||
<< "(" << m_tagId << ")"
|
||||
<< " m_wlsTeleTimeoutCounter =" << m_wlsTeleTimeoutCounter
|
||||
<< " s_maxWlsRetryCount=" << s_maxWlsRetryCount;
|
||||
if (m_wlsTeleTimeoutCounter == s_maxWlsRetryCount)
|
||||
{
|
||||
WARNING_LOG(m_logger) << getPlayerIdStr()
|
||||
<< ": Lost WLS connection to participant " << m_equipmentNumber
|
||||
<< "(" << m_tagId << ")";
|
||||
m_lastSeenDataCommunication = -1;
|
||||
m_wlsTeleTimeoutCounter = 0;
|
||||
m_nextSendCounter = 0;
|
||||
m_lastRecvCounter = 0;
|
||||
checkGpsOnOffTimeoutTimer(QDateTime::currentMSecsSinceEpoch());
|
||||
return;
|
||||
}
|
||||
sendNextWlsTelegram(true);
|
||||
}
|
||||
|
||||
uint8_t WlsParticipant::getNextSendCounter(bool bIsResend)
|
||||
{
|
||||
if (bIsResend)
|
||||
return m_nextSendCounter;
|
||||
++m_nextSendCounter;
|
||||
if (m_nextSendCounter == 0)
|
||||
++m_nextSendCounter;
|
||||
return m_nextSendCounter;
|
||||
}
|
||||
|
||||
bool WlsParticipant::isRecvCounterCorrect(uint8_t recvCounter)
|
||||
{
|
||||
// same as in tetra - the counter may not be monotonically ascending ...
|
||||
bool bRet = ((recvCounter == 0 && m_lastRecvCounter == 0) ||
|
||||
recvCounter != m_lastRecvCounter);
|
||||
m_lastRecvCounter = recvCounter;
|
||||
return bRet;
|
||||
}
|
||||
|
||||
TrainingAreaBase::TetraIcd::WlsParticipantState WlsParticipant::wlsParticipantState() const
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::WlsParticipantState ps;
|
||||
ps.equipmentNumber = m_equipmentNumber;
|
||||
ps.inBlockedArea = false;
|
||||
ps.gpsOn = isGpsMode();
|
||||
ps.lastSeen = m_lastSeenDataCommunication;
|
||||
ps.tacticalState = -1;
|
||||
ps.tagId = QString::fromLatin1(tagId());
|
||||
ps.voltage = voltage;
|
||||
|
||||
ps.relativePosition.x = m_relativePosition.x;
|
||||
ps.relativePosition.y = m_relativePosition.y;
|
||||
ps.relativePosition.z = m_relativePosition.z;
|
||||
|
||||
ps.relativePosition.wlsAreaId = m_relativePosition.wlsAreaId;
|
||||
ps.relativePosition.wlsZone = m_wlsZone;
|
||||
ps.relativePosition.wlsBuildingId = m_relativePosition.wlsBuildingId;
|
||||
ps.relativePosition.wlsRoomNumber = m_relativePosition.wlsRoomNumber;
|
||||
ps.relativePosition.playerOrientation = m_relativePosition.playerOrientation;
|
||||
ps.relativePosition.weaponOrientation = m_relativePosition.weaponOrientation;
|
||||
ps.relativePosition.isValid = m_relativePosition.isValid;
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, WlsParticipant::GpsMode gm)
|
||||
{
|
||||
switch (gm)
|
||||
{
|
||||
case WlsParticipant::GpsMode::On:
|
||||
return stream << "GpsMode::On";
|
||||
case WlsParticipant::GpsMode::Off:
|
||||
return stream << "GpsMode::Off";
|
||||
}
|
||||
return stream << "Unknown GpsMode (" << std::to_string(int(gm)) << ")";
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, WlsParticipant::DataCommunication dc)
|
||||
{
|
||||
switch (dc)
|
||||
{
|
||||
case WlsParticipant::DataCommunication::Unknown:
|
||||
return stream << "DataCommunication::Unknown";
|
||||
case WlsParticipant::DataCommunication::On:
|
||||
return stream << "DataCommunication::On";
|
||||
case WlsParticipant::DataCommunication::Off:
|
||||
return stream << "DataCommunication::Off";
|
||||
}
|
||||
return stream << "Unknown DataCommunication (" << std::to_string(int(dc)) << ")";
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/**
|
||||
* A single Participant
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
#include <QStringBuilder>
|
||||
#include <QTimer>
|
||||
//#include <QDebug>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "settings.h"
|
||||
|
||||
inline Q_CONSTEXPR double pi() { return M_PI; }
|
||||
inline Q_CONSTEXPR double deg2rad() { return pi() / 180.; }
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class Acknowledge_rx;
|
||||
class SfmMsgTetra_rx;
|
||||
class SfmMsgTetra_tx;
|
||||
|
||||
class WlsParticipant : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static int calcWlsZone(int wlsAreaId, int wlsBuildingId, int wlsRoomNumber)
|
||||
{
|
||||
if (wlsAreaId == 0 && wlsBuildingId == 0 && wlsRoomNumber == 0)
|
||||
return 3;
|
||||
if (wlsAreaId != 0 && wlsBuildingId == 1002 && wlsRoomNumber == 1001)
|
||||
return 4;
|
||||
if (wlsAreaId != 0 && wlsBuildingId == 0 && wlsRoomNumber == 0)
|
||||
return 5;
|
||||
|
||||
return 6;
|
||||
}
|
||||
static bool mustSendGpsOn(int wlsZoneOld, int wlsZoneNew)
|
||||
{
|
||||
// restart of service
|
||||
if (wlsZoneOld == -1)
|
||||
return wlsZoneNew <= 4;
|
||||
// normal operation
|
||||
return (wlsZoneOld >= 4 && wlsZoneNew <= 3);
|
||||
}
|
||||
static bool mustSendGpsOff(int wlsZoneOld, int wlsZoneNew)
|
||||
{
|
||||
// restart of service
|
||||
if (wlsZoneOld == -1)
|
||||
return wlsZoneNew >= 5;
|
||||
// normal operation
|
||||
return (wlsZoneOld <= 4 && wlsZoneNew >= 5);
|
||||
}
|
||||
|
||||
enum class GpsMode : char
|
||||
{
|
||||
Off,
|
||||
On
|
||||
};
|
||||
enum class DataCommunication : char
|
||||
{
|
||||
Unknown,
|
||||
Off,
|
||||
On
|
||||
};
|
||||
|
||||
struct RelativePosition
|
||||
{
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
double z = 0;
|
||||
int wlsAreaId = 0;
|
||||
int wlsBuildingId = 0; // = externalWlsId
|
||||
int wlsRoomNumber = 0;
|
||||
int playerOrientation = 0;
|
||||
int weaponOrientation = 0;
|
||||
bool isValid = false;
|
||||
|
||||
} ;
|
||||
|
||||
static bool comparePos (const RelativePosition &rhs, const RelativePosition &lhs )
|
||||
{
|
||||
double dx = ((lhs.x/deg2rad())-(rhs.x/deg2rad()));
|
||||
double dy = ((lhs.y/deg2rad())-rhs.y/deg2rad());
|
||||
double dz = (lhs.z-rhs.z);
|
||||
double dist = dx*dx + dy*dy + dz*dz;
|
||||
//qWarning() << "Compare two pos: square distance= " << QString::number(dist,'g',10);
|
||||
|
||||
bool ret = dist < Settings::wlsSquareDistanceMax()
|
||||
&& lhs.wlsAreaId == rhs.wlsAreaId
|
||||
&& lhs.wlsBuildingId == rhs.wlsBuildingId
|
||||
&& lhs.wlsRoomNumber == rhs.wlsRoomNumber;
|
||||
// && lhs.isValid == rhs.isValid;
|
||||
//qWarning() << "Return Value= " << ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
protected:
|
||||
explicit WlsParticipant(QObject *parent = nullptr);
|
||||
public:
|
||||
void setPosition(const RelativePosition &newPosition, qint64 tsCurrentPos);
|
||||
bool positionIsValid() const { return m_relativePosition.isValid; }
|
||||
qint64 positionTimestamp() const { return m_lastValidPosition; }
|
||||
RelativePosition position() const { return m_relativePosition; }
|
||||
bool isGpsMode() const { return m_gpsMode == WlsParticipant::GpsMode::On; }
|
||||
// returns true when timeout is reached
|
||||
void checkGpsOnOffTimeoutTimer(qint64 tNow);
|
||||
void setLastSeenWlsTelegramTime(qint64 ts);
|
||||
qint64 lastSeenWlsTelegramTime() const { return m_lastSeenDataCommunication; }
|
||||
|
||||
void setWlsZone(int newWlsZone);
|
||||
int wlsZone() const { return m_wlsZone; }
|
||||
|
||||
QByteArray getPlayerIdStr() const;
|
||||
|
||||
const QByteArray &tagId() const { return m_tagId; }
|
||||
void setEquipmentNumber(int newEquipmentNumber);
|
||||
int equipmentNumber() const { return m_equipmentNumber; }
|
||||
int exerciseId() const { return m_exerciseId; }
|
||||
void setExerciseId(int exerciseId) { m_exerciseId = exerciseId; }
|
||||
bool isDataCommunicationPossible() const { return m_dataCommunication == DataCommunication::On; }
|
||||
DataCommunication dataCommunication() const { return m_dataCommunication; }
|
||||
GpsMode gpsMode() const { return m_gpsMode; }
|
||||
|
||||
bool handleSfmRxData(const SfmMsgTetra_rx *sfmTele);
|
||||
bool isSfmRxDataAvailable() const { return m_bSfmPayloadReady; }
|
||||
uint8_t nextSfmRxDataFragmentCounter() const { return m_sfmRxPayloadFragmentNumber; }
|
||||
const QByteArray &sfmRxData() const { return m_sfmRxPayloadData; }
|
||||
|
||||
static bool calcChunkCountAndLen(const QByteArray &data, int &chunkCount, int &chunkLen);
|
||||
void sendBinarySfmData(const std::vector<unsigned char> &data);
|
||||
void handleWlsAck(const Acknowledge_rx *ack);
|
||||
|
||||
TrainingAreaBase::TetraIcd::WlsParticipantState wlsParticipantState() const;
|
||||
|
||||
// TODO
|
||||
double voltage = 0;
|
||||
qint64 lastEquipmentNumberRequest = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void positionChanged(WlsParticipant *p);
|
||||
void gpsOnOffChanged(WlsParticipant *p);
|
||||
void wlsBinarySfmDataComplete(WlsParticipant *p);
|
||||
void dataCommunicationStateChanged(WlsParticipant *p);
|
||||
void dataForWls(WlsParticipant *p, const SfmMsgTetra_tx &data);
|
||||
private Q_SLOTS:
|
||||
void checkSendTele();
|
||||
void sendNextWlsTelegram(bool bIsResend);
|
||||
void onWlsTeleTimeout();
|
||||
private:
|
||||
uint8_t getNextSendCounter(bool bIsResend);
|
||||
bool isRecvCounterCorrect(uint8_t recvCounter);
|
||||
private:
|
||||
struct WlsPayloadData
|
||||
{
|
||||
QByteArray payload;
|
||||
uint8_t retryCount = 0;
|
||||
uint8_t fragmentCount = 0;
|
||||
uint8_t fragmentNumber = 0;
|
||||
};
|
||||
|
||||
friend class WlsParticipantHandler;
|
||||
QByteArray m_tagId;
|
||||
QByteArray m_sfmRxPayloadData;
|
||||
QQueue<WlsPayloadData> m_currentFragmentedTeleQueue;
|
||||
QQueue<WlsPayloadData> m_wlsQueue;
|
||||
|
||||
uint8_t m_sfmRxPayloadFragmentNumber = 0;
|
||||
bool m_bSfmPayloadReady = false;
|
||||
|
||||
int m_equipmentNumber = 0;
|
||||
int m_exerciseId = 0;
|
||||
// zone 1 and 2 will never be met here - far outside wls area
|
||||
int m_wlsZone = -1; // 1 .. 6
|
||||
|
||||
RelativePosition m_relativePosition;
|
||||
GpsMode m_gpsMode = GpsMode::On;
|
||||
DataCommunication m_dataCommunication = DataCommunication::Unknown;
|
||||
qint64 m_lastSeenDataCommunication = 0;
|
||||
qint64 m_lastValidPosition = 0;
|
||||
qint64 m_lastSeenPosition = 0;
|
||||
|
||||
QTimer *m_wlsTeleTimeoutTimer = nullptr;
|
||||
int m_wlsTeleTimeoutCounter = 0;
|
||||
uint8_t m_nextSendCounter = 0;
|
||||
uint8_t m_lastRecvCounter = 0;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, WlsParticipant::GpsMode gm);
|
||||
std::ostream &operator<<(std::ostream &stream, WlsParticipant::DataCommunication dc);
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Handle Participants which are available in the system (map between Wls id and ParticipantId)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#include "wls_participant_handler.h"
|
||||
#include "web_socket_handler.h"
|
||||
#include "wls_handler.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
WlsParticipantHandler::WlsParticipantHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_gpsOnOffTimeoutTimer(new QTimer(this))
|
||||
, m_logger(getLogger("WlsParticipantHandler"))
|
||||
{
|
||||
connect(m_gpsOnOffTimeoutTimer, &QTimer::timeout, this, &WlsParticipantHandler::onGpsOnOffTimeoutTimer);
|
||||
m_gpsOnOffTimeoutTimer->setSingleShot(false);
|
||||
m_gpsOnOffTimeoutTimer->start(1000);
|
||||
}
|
||||
|
||||
WlsParticipantHandler::~WlsParticipantHandler()
|
||||
{
|
||||
qDeleteAll(m_participantsByEqId);
|
||||
}
|
||||
|
||||
void WlsParticipantHandler::init(WebSocketHandler *wsHandler,
|
||||
WlsHandler *wlsHandler)
|
||||
{
|
||||
m_wsHandler = wsHandler;
|
||||
m_wlsHandler = wlsHandler;
|
||||
}
|
||||
|
||||
WlsParticipant *WlsParticipantHandler::createNewParticipant(const QByteArray &tagId)
|
||||
{
|
||||
delete m_participantsByTagId.take(tagId);
|
||||
WlsParticipant *p = new WlsParticipant;
|
||||
connect(p, &WlsParticipant::gpsOnOffChanged,
|
||||
m_wlsHandler, &WlsHandler::sendGpsMode);
|
||||
connect(p, &WlsParticipant::dataForWls,
|
||||
m_wlsHandler, QOverload<const WlsParticipant *,const SfmMessage_tx &>::of(&WlsHandler::sendWlsMessage));
|
||||
connect(p, &WlsParticipant::gpsOnOffChanged,
|
||||
m_wsHandler, &WebSocketHandler::sendGpsPositionUsage);
|
||||
connect(p, &WlsParticipant::positionChanged,
|
||||
m_wsHandler, &WebSocketHandler::sendPosition);
|
||||
connect(p, &WlsParticipant::wlsBinarySfmDataComplete,
|
||||
m_wsHandler, &WebSocketHandler::sendBinarySfmData);
|
||||
connect(p, &WlsParticipant::dataCommunicationStateChanged,
|
||||
m_wsHandler, &WebSocketHandler::sendDataCommunicationState);
|
||||
|
||||
p->m_tagId = tagId;
|
||||
m_participantsByTagId[tagId] = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
void WlsParticipantHandler::setEquipmentNumber(WlsParticipant *participant, int equipmentNumber)
|
||||
{
|
||||
participant->setExerciseId(m_equipNo2ExerciseId.value(equipmentNumber));
|
||||
|
||||
auto it = m_participantsByEqId.find(participant->equipmentNumber());
|
||||
// remove old assignment
|
||||
if (it != m_participantsByEqId.end())
|
||||
m_participantsByEqId.erase(it);
|
||||
|
||||
participant->setEquipmentNumber(equipmentNumber);
|
||||
m_participantsByEqId[equipmentNumber] = participant;
|
||||
}
|
||||
|
||||
WlsParticipant *WlsParticipantHandler::participantByTagId(const QByteArray &tagId) const
|
||||
{
|
||||
return m_participantsByTagId.value(tagId);
|
||||
}
|
||||
|
||||
void WlsParticipantHandler::exerciseEquipmentAssignmentChanged(const std::unordered_map<int, std::unordered_set<int>> &eea)
|
||||
{
|
||||
m_equipNo2ExerciseId.clear();
|
||||
for (const auto &itEx : eea)
|
||||
{
|
||||
const int curExerciseId = itEx.first;
|
||||
const auto &assignment = itEx.second;
|
||||
for (int eqId : assignment)
|
||||
{
|
||||
m_equipNo2ExerciseId[eqId] = curExerciseId;
|
||||
WlsParticipant *p = m_participantsByEqId.value(eqId);
|
||||
if (!p)
|
||||
continue;
|
||||
p->setExerciseId(curExerciseId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WlsParticipantHandler::handleWlsBinarySfmData(const TrainingAreaBase::TetraIcd::WlsBinarySfmData &data)
|
||||
{
|
||||
WlsParticipant *p = m_participantsByEqId.value(data.header.equipmentNumber);
|
||||
if (!p)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Got binary sfm data for participant with eqId " << data.header.equipmentNumber
|
||||
<< " but could not find this participant!";
|
||||
return;
|
||||
}
|
||||
p->sendBinarySfmData(data.payload);
|
||||
}
|
||||
|
||||
void WlsParticipantHandler::onGpsOnOffTimeoutTimer()
|
||||
{
|
||||
const qint64 tNow = QDateTime::currentMSecsSinceEpoch();
|
||||
for (WlsParticipant *p : qAsConst(m_participantsByTagId))
|
||||
p->checkGpsOnOffTimeoutTimer(tNow);
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Handle Participants which are available in the system (map between Wls id and ParticipantId)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "logger.h"
|
||||
#include "wls_participant.h"
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
class WebSocketHandler;
|
||||
class WlsHandler;
|
||||
|
||||
class WlsParticipantHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WlsParticipantHandler(QObject *parent = nullptr);
|
||||
~WlsParticipantHandler();
|
||||
|
||||
void init(WebSocketHandler *wsHandler,
|
||||
WlsHandler *wlsHandler);
|
||||
|
||||
// ownership stays here
|
||||
WlsParticipant *createNewParticipant(const QByteArray &tagId);
|
||||
void setEquipmentNumber(WlsParticipant *participant, int equipmentNumber);
|
||||
|
||||
WlsParticipant *participantByTagId(const QByteArray &tagId) const;
|
||||
const QHash<QByteArray, WlsParticipant *> &participants() const { return m_participantsByTagId; }
|
||||
|
||||
void exerciseEquipmentAssignmentChanged(const std::unordered_map<int, std::unordered_set<int>> &eea);
|
||||
void handleWlsBinarySfmData(const TrainingAreaBase::TetraIcd::WlsBinarySfmData &data);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onGpsOnOffTimeoutTimer();
|
||||
private:
|
||||
WebSocketHandler *m_wsHandler = nullptr;
|
||||
WlsHandler *m_wlsHandler = nullptr;
|
||||
|
||||
QHash<QByteArray, WlsParticipant *> m_participantsByTagId;
|
||||
QHash<int, WlsParticipant *> m_participantsByEqId;
|
||||
QHash<int, int> m_equipNo2ExerciseId;
|
||||
|
||||
QTimer *m_gpsOnOffTimeoutTimer;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
} // namespace WirelessLocationSystem
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<service namespace="WirelessLocationSystem" procNames="WirelessLocationSystem" abbreviation="WLS" exerciseDependency="Global" groups="Core">
|
||||
<rcs>
|
||||
<Id Id="$Id: ea_export_script.txt 28914 2016-08-23 12:08:05Z ehrlicher $"/>
|
||||
<HeadURL HeadURL="$HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Common/CodeGen/ea_export_script.txt $"/>
|
||||
<Author Author="$Author: ehrlicher $"/>
|
||||
</rcs>
|
||||
<tables/>
|
||||
<dbServiceStructs/>
|
||||
<topicTypes>
|
||||
<topicType name="dummyStruct">
|
||||
<column name="id" idlType="long"/>
|
||||
</topicType>
|
||||
</topicTypes>
|
||||
<functionTopics/>
|
||||
<enumerations>
|
||||
<enumeration name="dummyEnum">
|
||||
<attribute key="unknown" value=""/>
|
||||
</enumeration>
|
||||
</enumerations>
|
||||
</service>
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Messages from/to a SFM (passed through Wls message PayloadMobileToHost / PayloadHostToMobile
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#include "wls_sfm_message.h"
|
||||
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
/// -------------- SfmMsgGpsControl_tx --------------
|
||||
|
||||
QByteArray SfmMsgGpsControl_tx::getPayload() const
|
||||
{
|
||||
QByteArray payload;
|
||||
payload.append(isGpsOn() ? 1 : 0);
|
||||
return payload;
|
||||
}
|
||||
|
||||
/// ---------- SfmMsgEquipmentId_rx ----------
|
||||
bool SfmMsgEquipmentId::setPayload(const QByteArray &p_payload)
|
||||
{
|
||||
if (p_payload.size() != s_payloadSize)
|
||||
return false;
|
||||
|
||||
m_equipmentNumber = p_payload[0] << 8;
|
||||
m_equipmentNumber |= ((unsigned short)p_payload[1]) & 0xFF;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray SfmMsgEquipmentId::getPayload() const
|
||||
{
|
||||
QByteArray retVal(3, Qt::Uninitialized);
|
||||
retVal[0] = (m_equipmentNumber >> 8) & 0xFF;
|
||||
retVal[1] = m_equipmentNumber & 0xFF;
|
||||
retVal[2] = 0;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// ---------- SfmMsgTacticalState_rx ----------
|
||||
bool SfmMsgTacticalState_rx::setPayload(const QByteArray &p_payload)
|
||||
{
|
||||
if (p_payload.size() != s_payloadSize)
|
||||
return false;
|
||||
m_tacticalState = p_payload[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// ---------- SfmMsgTetra ----------
|
||||
bool SfmMsgTetra_rx::setPayload(const QByteArray &p_data)
|
||||
{
|
||||
if (p_data.size() < 3)
|
||||
return false;
|
||||
|
||||
m_sendCounter = p_data[0];
|
||||
m_fragmentByte = p_data[1];
|
||||
m_payload = p_data.mid(2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------- Acknowledge_rx ----------
|
||||
bool Acknowledge_rx::setPayload(const QByteArray &p_data)
|
||||
{
|
||||
if (p_data.size() != s_payloadSize)
|
||||
return false;
|
||||
m_recvCounter = p_data[0];
|
||||
m_ackState = p_data[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------- Acknowledge_tx ----------
|
||||
QByteArray Acknowledge_tx::getPayload() const
|
||||
{
|
||||
QByteArray ba(2, Qt::Uninitialized);
|
||||
ba[0] = m_recvCounter;
|
||||
ba[1] = m_ackState;
|
||||
return ba;
|
||||
}
|
||||
|
||||
|
||||
/// ---------- SfmMsgLizensStatus ----------
|
||||
bool SfmMsgLicenseState_rx::setPayload(const QByteArray &p_data)
|
||||
{
|
||||
if (p_data.size() != s_payloadSize)
|
||||
return false;
|
||||
|
||||
if (p_data[0] != 0 && p_data[0] != 1)
|
||||
return false;
|
||||
m_isLicensed = p_data[0] ? true : false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* Messages from/to a SFM (passed through Wls message PayloadMobileToHost / PayloadHostToMobile
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <WirelessLocationSystem/sfmconverter_export.h>
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
/**
|
||||
* \class SfmMessage
|
||||
*
|
||||
* \brief Oberklasse fuer Nachrichten von/an das SFM
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMessage
|
||||
{
|
||||
public:
|
||||
enum class MsgId
|
||||
{
|
||||
TacticalState = 37,
|
||||
SfmRestartNotification = 76,
|
||||
GpsControl = 79,
|
||||
RequestGpsControl = 79 | 0x8000, // RTR bit set
|
||||
EquipmentId = 478,
|
||||
RequestEquipmentId = 478 | 0x8000, // RTR bit set
|
||||
Acknowledge = 1098,
|
||||
LicenseState = 1099,
|
||||
TetraMessage = 1100
|
||||
};
|
||||
|
||||
protected:
|
||||
explicit SfmMessage(MsgId id)
|
||||
: m_id(id)
|
||||
{}
|
||||
|
||||
public:
|
||||
virtual ~SfmMessage() = default;
|
||||
|
||||
MsgId getId() const { return m_id; }
|
||||
private:
|
||||
MsgId m_id;
|
||||
};
|
||||
|
||||
/// --------------SfmMessage_tx--------------
|
||||
/**
|
||||
* \class SfmMessage_tx
|
||||
*
|
||||
* \brief Oberklasse fuer sendbare Nachrichten
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMessage_tx : public SfmMessage
|
||||
{
|
||||
public:
|
||||
using SfmMessage::SfmMessage;
|
||||
|
||||
/**
|
||||
* \brief Interface zur Abfrage der Payload
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*
|
||||
* Diese Methode gibt der reine Payload der Nachricht wieder.
|
||||
* Default: Leeres ByteArray (keine Parameter)
|
||||
*
|
||||
* \return Ein ByteArray welches die vollstaendige Payload enthaelt
|
||||
*/
|
||||
virtual QByteArray getPayload() const = 0;
|
||||
};
|
||||
|
||||
/// --------------SfmMessage_rx--------------
|
||||
/**
|
||||
* \class SfmMessage_rx
|
||||
*
|
||||
* \brief Oberklasse fuer empfangbare Nachrichten
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMessage_rx : public SfmMessage
|
||||
{
|
||||
public:
|
||||
using SfmMessage::SfmMessage;
|
||||
|
||||
/**
|
||||
* \brief Interface zur Püfung und Aufschlüsselung der Payload
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*
|
||||
* Standard Implementierung geht von einer leeren Payload aus
|
||||
*
|
||||
* \param payload Die zu verarbeitende Payload
|
||||
*
|
||||
* \return true: Payload konnte korrekt verarbeitet werden
|
||||
*/
|
||||
virtual bool setPayload(const QByteArray &payload) = 0;
|
||||
};
|
||||
/// ########## Specific Messages ##########
|
||||
|
||||
/// ---------- SfmMsgGpsControl_tx ----------
|
||||
/**
|
||||
* \class SfmMsgGpsControl_tx
|
||||
*
|
||||
* \brief Setzen der GPS-Control
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMsgGpsControl_tx final : public SfmMessage_tx
|
||||
{
|
||||
public:
|
||||
explicit SfmMsgGpsControl_tx(bool gpsOn = false)
|
||||
: SfmMessage_tx(SfmMessage::MsgId::GpsControl)
|
||||
, m_gpsOn(gpsOn)
|
||||
{}
|
||||
|
||||
QByteArray getPayload() const override;
|
||||
|
||||
void setGpsOn(bool bOn) { m_gpsOn = bOn; }
|
||||
bool isGpsOn() const { return m_gpsOn; }
|
||||
|
||||
private:
|
||||
bool m_gpsOn;
|
||||
};
|
||||
|
||||
/// ---------- SfmMsgRequestGpsControl_rx ----------
|
||||
/**
|
||||
* \class SfmMsgRequestGpsControl_rx
|
||||
*
|
||||
* \brief Request from SFM to get current GPS mode
|
||||
*
|
||||
* description
|
||||
*
|
||||
* \author Ch. Ehrlicher
|
||||
*/
|
||||
class SfmMsgRequestGpsControl_rx final : public SfmMessage_rx
|
||||
{
|
||||
public:
|
||||
SfmMsgRequestGpsControl_rx()
|
||||
: SfmMessage_rx(SfmMessage::MsgId::RequestGpsControl)
|
||||
{}
|
||||
|
||||
bool setPayload(const QByteArray &payload) override
|
||||
{
|
||||
return (payload.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// ---------- SfmMsgRequestEquipmentId_tx ----------
|
||||
/**
|
||||
* \class SfmMsgRequestEquipmentId_tx
|
||||
*
|
||||
* \brief Anfrage der AusstattungsNr
|
||||
*
|
||||
* description
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMsgRequestEquipmentId_tx final : public SfmMessage_tx
|
||||
{
|
||||
public:
|
||||
SfmMsgRequestEquipmentId_tx()
|
||||
: SfmMessage_tx(SfmMessage::MsgId::RequestEquipmentId)
|
||||
{}
|
||||
|
||||
QByteArray getPayload() const override
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
};
|
||||
|
||||
/// ---------- SfmMsgEquipmentId_rx ----------
|
||||
/**
|
||||
* \class SfmMsgEquipmentId_rx
|
||||
*
|
||||
* \brief Rueckgabe der AusstattungsNr
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SFMCONVERTER_EXPORT SfmMsgEquipmentId final : public SfmMessage_rx, public SfmMessage_tx
|
||||
{
|
||||
public:
|
||||
SfmMsgEquipmentId()
|
||||
: SfmMessage_rx(SfmMessage::MsgId::EquipmentId)
|
||||
, SfmMessage_tx(SfmMessage::MsgId::EquipmentId)
|
||||
{}
|
||||
|
||||
bool setPayload(const QByteArray &) override;
|
||||
QByteArray getPayload() const override;
|
||||
|
||||
static constexpr int s_payloadSize = 3;
|
||||
|
||||
unsigned short getEquipmentNumber() const
|
||||
{
|
||||
return m_equipmentNumber;
|
||||
}
|
||||
void setEquipmentNumber(quint16 equipmentNumber) { m_equipmentNumber = equipmentNumber; }
|
||||
|
||||
private:
|
||||
unsigned short m_equipmentNumber = 0;
|
||||
};
|
||||
|
||||
/// ---------- SfmMsgSfmRestart_rx ----------
|
||||
/**
|
||||
* \class SfmMsgSfmRestart_rx
|
||||
*
|
||||
* \brief Anzeigen eines SFM-Restarts
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMsgSfmRestart_rx final : public SfmMessage_rx
|
||||
{
|
||||
public:
|
||||
SfmMsgSfmRestart_rx()
|
||||
: SfmMessage_rx(SfmMessage::MsgId::SfmRestartNotification)
|
||||
{}
|
||||
bool setPayload(const QByteArray &payload) override
|
||||
{
|
||||
return (payload.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
/// ---------- SfmMsgTacticalState_rx ----------
|
||||
/**
|
||||
* \class SfmMsgTacticalState_rx
|
||||
*
|
||||
* \brief Setzen des VBGs eines Teilnehmers
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMsgTacticalState_rx final : public SfmMessage_rx
|
||||
{
|
||||
public:
|
||||
SfmMsgTacticalState_rx()
|
||||
: SfmMessage_rx(SfmMessage::MsgId::TacticalState)
|
||||
{}
|
||||
|
||||
static constexpr int s_payloadSize = 1;
|
||||
|
||||
bool setPayload(const QByteArray &) override;
|
||||
|
||||
unsigned char getTacticalState() const { return m_tacticalState; }
|
||||
|
||||
private:
|
||||
unsigned char m_tacticalState = 0;
|
||||
};
|
||||
|
||||
/// ---------- SfmMsgTetra ----------
|
||||
/**
|
||||
* \class SfmMsgTetra
|
||||
*
|
||||
* \brief Eine Tetra-Nachricht die zur Zentrale weitergereicht werden muss
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMsgTetra_rx final : public SfmMessage_rx
|
||||
{
|
||||
public:
|
||||
SfmMsgTetra_rx()
|
||||
: SfmMessage_rx(SfmMessage::MsgId::TetraMessage)
|
||||
{}
|
||||
|
||||
bool setPayload(const QByteArray &p_data) override;
|
||||
|
||||
uint8_t sendCounter() const { return m_sendCounter; }
|
||||
bool isFragmented() const { return fragmentCount() > 1; }
|
||||
uint8_t fragmentNumber() const { return m_fragmentByte & 0x0f; }
|
||||
uint8_t fragmentCount() const { return (m_fragmentByte & 0xf0) >> 4;}
|
||||
|
||||
const QByteArray &payload() const { return m_payload; }
|
||||
|
||||
private:
|
||||
QByteArray m_payload;
|
||||
uint8_t m_sendCounter = 0;
|
||||
uint8_t m_fragmentByte = 0;
|
||||
};
|
||||
|
||||
class SfmMsgTetra_tx final : public SfmMessage_tx
|
||||
{
|
||||
public:
|
||||
explicit SfmMsgTetra_tx(uint8_t sendCounter, int fragmentCount, int fragmentNumber, const QByteArray &data)
|
||||
: SfmMessage_tx(SfmMessage::MsgId::TetraMessage)
|
||||
{
|
||||
m_data.reserve(data.size() + 2);
|
||||
m_data += sendCounter;
|
||||
m_data += fragmentByte(fragmentCount, fragmentNumber);
|
||||
m_data += data;
|
||||
}
|
||||
|
||||
static char fragmentByte(int fragmentCount, int fragmentNumber)
|
||||
{
|
||||
Q_ASSERT(fragmentCount >= 0 && fragmentCount < 16);
|
||||
Q_ASSERT(fragmentNumber >= 0 && fragmentNumber <= fragmentCount);
|
||||
return ((fragmentCount & 0x0f) << 4 ) | (fragmentNumber & 0x0f);
|
||||
}
|
||||
QByteArray getPayload() const override { return m_data; }
|
||||
private:
|
||||
QByteArray m_data;
|
||||
};
|
||||
|
||||
/// ---------- Ack/NAck ----------
|
||||
class Acknowledge_rx final : public SfmMessage_rx
|
||||
{
|
||||
public:
|
||||
Acknowledge_rx()
|
||||
: SfmMessage_rx(SfmMessage::MsgId::Acknowledge)
|
||||
{}
|
||||
|
||||
static constexpr int s_payloadSize = 2;
|
||||
|
||||
bool setPayload(const QByteArray &p_data) override;
|
||||
uint8_t recvCounter() const { return m_recvCounter; }
|
||||
bool isOk() const { return m_ackState == 0; }
|
||||
bool isCrcError() const { return (m_ackState & 0x20) == 0x20; }
|
||||
bool isFragmentError() const { return (m_ackState & 0x10) == 0x10; }
|
||||
uint8_t missingFragment() const { return (m_ackState & 0x0f)!= 0; }
|
||||
private:
|
||||
uint8_t m_recvCounter = 0;
|
||||
uint8_t m_ackState = 0;
|
||||
};
|
||||
|
||||
class Acknowledge_tx final : public SfmMessage_tx
|
||||
{
|
||||
public:
|
||||
explicit Acknowledge_tx()
|
||||
: SfmMessage_tx(SfmMessage::MsgId::Acknowledge)
|
||||
{}
|
||||
|
||||
QByteArray getPayload() const override;
|
||||
|
||||
void setRecvCounter(uint8_t recvCounter) { m_recvCounter = recvCounter; }
|
||||
void setCrcError() { m_ackState |= 0x20; }
|
||||
void setFragmentError(uint8_t missingFrag) { m_ackState |= (0x10 | (0x0f & missingFrag)); }
|
||||
private:
|
||||
uint8_t m_recvCounter = 0;
|
||||
uint8_t m_ackState = 0;
|
||||
};
|
||||
|
||||
/// ---------- MsgLizensStatus ----------
|
||||
/**
|
||||
* \class SfmMsgLizensStatus
|
||||
*
|
||||
* \brief Eine Lizenzstatus-Nachricht die zur Zentrale weitergereicht werden muss
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*/
|
||||
class SfmMsgLicenseState_rx final : public SfmMessage_rx
|
||||
{
|
||||
public:
|
||||
SfmMsgLicenseState_rx()
|
||||
: SfmMessage_rx(SfmMessage::MsgId::LicenseState)
|
||||
{}
|
||||
|
||||
static constexpr int s_payloadSize = 1;
|
||||
|
||||
bool setPayload(const QByteArray &p_data) override;
|
||||
bool isLicensed() const { return m_isLicensed; }
|
||||
|
||||
private:
|
||||
bool m_isLicensed = false;
|
||||
};
|
||||
|
||||
}//namespace WirelessLocationSystem
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Convert SfmMessages to Wls representation and vice versa
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#include "wls_sfm_message_converter.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <LtsUtilsCore/sfm_bytemessage_converter.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
namespace SfmMessageConverter
|
||||
{
|
||||
|
||||
/**
|
||||
* \brief Alloziiert auf Basis der Parameter ein SfmMessage_rx-Kindobjekt
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*
|
||||
* \param id Die ID der Nachricht
|
||||
*
|
||||
* \return Falls möglich ein entsprechendes Kindobjekt, sonst einen Null-Pointer
|
||||
*/
|
||||
|
||||
static SfmMessage_rx *getSpecificSfmMessage(SfmMessage::MsgId p_id)
|
||||
{
|
||||
switch (p_id)
|
||||
{
|
||||
case SfmMessage::MsgId::SfmRestartNotification:
|
||||
return new SfmMsgSfmRestart_rx;
|
||||
case SfmMessage::MsgId::EquipmentId:
|
||||
return new SfmMsgEquipmentId;
|
||||
case SfmMessage::MsgId::TacticalState:
|
||||
return new SfmMsgTacticalState_rx;
|
||||
case SfmMessage::MsgId::LicenseState:
|
||||
return new SfmMsgLicenseState_rx;
|
||||
case SfmMessage::MsgId::TetraMessage:
|
||||
return new SfmMsgTetra_rx;
|
||||
case SfmMessage::MsgId::Acknowledge:
|
||||
return new Acknowledge_rx;
|
||||
case SfmMessage::MsgId::RequestGpsControl:
|
||||
return new SfmMsgRequestGpsControl_rx;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QByteArray generateTxMessage(const SfmMessage_tx &message)
|
||||
{
|
||||
// Converting to LtsUtils::SfmMessage
|
||||
LtsUtils::SfmMessage msg;
|
||||
|
||||
msg.setId(static_cast<int>(message.getId()) & 0x7FFF);
|
||||
msg.setRtr(static_cast<int>(message.getId()) & 0x8000);
|
||||
msg.setPayload(message.getPayload());
|
||||
|
||||
return LtsUtils::byteStreamFromSfmMessage(msg).toHex();
|
||||
}
|
||||
|
||||
QSharedPointer<SfmMessage_rx> generateRxMessage(const QByteArray &data)
|
||||
{
|
||||
LtsUtils::SfmMessage message;
|
||||
SfmMessage_rx *msg = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
message = LtsUtils::byteStreamToSfmMessage(QByteArray::fromHex(data));
|
||||
}
|
||||
catch (const LtsUtils::ConversionError &ex)
|
||||
{
|
||||
ERROR_LOG(getLogger("SfmMessageConverter")) << "Error parsing bytestream: " << data << " Exception: " << ex.what();
|
||||
return QSharedPointer<SfmMessage_rx>(msg);
|
||||
}
|
||||
|
||||
/// Erzeugen der entsprechenden Kind-Klasse from LtsUtils::SfmMessage
|
||||
SfmMessage::MsgId msgId = static_cast<SfmMessage::MsgId>(message.id() | (message.rtr() << 15));
|
||||
msg = getSpecificSfmMessage(msgId);
|
||||
if (!msg)
|
||||
{
|
||||
ERROR_LOG(getLogger("SfmMessageConverter")) << "Error creating SfmMessage for message with id: " << message.id()
|
||||
<< ", rtr: " << message.rtr()
|
||||
<< ", combined: 0x" << QString::number(int(msgId));
|
||||
}
|
||||
else if (!msg->setPayload(message.payload()))
|
||||
{
|
||||
delete msg;
|
||||
msg = nullptr;
|
||||
}
|
||||
|
||||
return QSharedPointer<SfmMessage_rx>(msg);
|
||||
}
|
||||
|
||||
} // namespace SfmMessageConverter
|
||||
|
||||
} // namespace WirelessLocationSystem
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Convert SfmMessages to Wls representation and vice versa
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2017-01-10
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "wls_sfm_message.h"
|
||||
|
||||
namespace WirelessLocationSystem
|
||||
{
|
||||
|
||||
namespace SfmMessageConverter
|
||||
{
|
||||
/**
|
||||
* \brief Erzeugt aus einem SfmMessage_tx-Objekt ein QByteArray mit dem entsprechenden Telegrammaufbau
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*
|
||||
* Bei der Erzeugen werden alle notwendigen Operationen auf dem Telegram durchgeführt, so das es dem
|
||||
* Telegramaufbau entspricht
|
||||
*
|
||||
* \param message Die Nachricht aus der das Telegram gebaut werden soll
|
||||
*
|
||||
* \return Das Telegram als QByteArray
|
||||
*/
|
||||
SFMCONVERTER_EXPORT QByteArray generateTxMessage(const SfmMessage_tx &message);
|
||||
|
||||
/**
|
||||
* \brief Versucht aus einem QByteArray ein SfmMessage_rx-Kindobjekt zu erzeugen
|
||||
*
|
||||
* \author B. Ziegler
|
||||
*
|
||||
* Prueft das uebergebene QByteArray auf Korrektheit und erzeugt daraus eine Ableitung der SfmMessage_rx-Klasse
|
||||
*
|
||||
* \param data Enthaelt die Rohdaten der Sfm-Nachricht
|
||||
*
|
||||
* \return Wenn das QByteArray korrekt war eine entsprechende Ableitung der SfmMessage_rx, sonst einen Null-Pointer
|
||||
*/
|
||||
QSharedPointer<SfmMessage_rx> generateRxMessage(const QByteArray &data);
|
||||
}
|
||||
}//namespace WirelessLocationSystem
|
||||
Reference in new issue
Block a user