Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+231
@@ -0,0 +1,231 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
# Uncomment if necessary
|
||||
#ADD_SUBDIRECTORY(UnitTests)
|
||||
#ADD_SUBDIRECTORY(ModuleTests)
|
||||
|
||||
INCLUDE(MacroRTIDDSGen)
|
||||
INCLUDE(MacroCodeGenIdl)
|
||||
|
||||
SET(xml_FILE taboc_service.xml)
|
||||
|
||||
#
|
||||
# --- Library svcTabocCommon --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_common
|
||||
logger.cpp
|
||||
logger.h
|
||||
taboc_export.h
|
||||
)
|
||||
|
||||
# Generate taboc_enums.idl / taboc.idl
|
||||
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
|
||||
|
||||
# Generate *.cpp/*.hpp from idl
|
||||
RTIDDSGEN(SRCS_common taboc_enums.idl)
|
||||
RTIDDSGEN(SRCS_common taboc.idl)
|
||||
|
||||
# Generate taboc_dt[s,c]_types.h, taboc_ft[s,c]_types.h
|
||||
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
|
||||
# generate taboc_[d|f]tc.h
|
||||
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(AAR SV WS svcTabocCommon SHARED ${SRCS_common})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(svcTabocCommon
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
NGL_TARGET_LINK_LIBRARIES(svcTabocCommon
|
||||
PRIVATE
|
||||
RTI::nddscpp2
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
ddscore
|
||||
NGLLogger
|
||||
LtsUtilsCoreHeaderOnly
|
||||
svcPdmCommon
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcTabocCommon)
|
||||
|
||||
#
|
||||
# --- Library svcTabocClient --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_client
|
||||
taboc_export.h
|
||||
)
|
||||
# generate taboc_[d|f]tc_impl.[cpp|h]
|
||||
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(AAR SV WS svcTabocClient SHARED ${SRCS_client})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(svcTabocClient
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(svcTabocClient
|
||||
PRIVATE
|
||||
RTI::nddscpp2
|
||||
ddsconnector
|
||||
PUBLIC
|
||||
svcTabocCommon
|
||||
ddsftc
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcTabocClient)
|
||||
|
||||
#
|
||||
# --- Library svcTabocServer --------------------------------------------------------------------------------
|
||||
#
|
||||
SET(SRCS_server
|
||||
taboc_export.h
|
||||
)
|
||||
# generate taboc_[d|f]ts.h, taboc_[d|f]ts_impl.[cpp|h]
|
||||
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE})
|
||||
# Generate taboc_db_impl.cpp/.h, taboc_db.h
|
||||
#CODEGEN_DATABASE(SRCS_server ${xml_FILE})
|
||||
|
||||
NGL_ADD_LIBRARY(AAR SV svcTabocServer SHARED ${SRCS_server})
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(svcTabocServer
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../
|
||||
)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(svcTabocServer
|
||||
PRIVATE
|
||||
RTI::nddscpp2
|
||||
ddsconnector
|
||||
PUBLIC
|
||||
svcTabocCommon
|
||||
ngldb
|
||||
ddsfts
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcTabocServer)
|
||||
|
||||
#
|
||||
# --- Topic Type Plugin --------------------------------------------------------------
|
||||
#
|
||||
IF(BUILD_SV)
|
||||
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
|
||||
ENDIF()
|
||||
|
||||
#
|
||||
# --- Executable svcTrainingAreaBaseOC (sTABOC) -------------------------------------------------
|
||||
#
|
||||
SET(SRCS_exe
|
||||
dds_handler.cpp
|
||||
dds_handler.h
|
||||
main.cpp
|
||||
main_handler.cpp
|
||||
main_handler.h
|
||||
connection_handler.cpp
|
||||
connection_handler.h
|
||||
connection_handler_tcp.cpp
|
||||
connection_handler_tcp.h
|
||||
connection_handler_serial.cpp
|
||||
connection_handler_serial.h
|
||||
connection_handler_tcp_ttci.cpp
|
||||
connection_handler_tcp_ttci.h
|
||||
websocket_handler.cpp
|
||||
websocket_handler.h
|
||||
canag_handler.cpp
|
||||
canag_handler.h
|
||||
handler_aes_data.cpp
|
||||
handler_aes_data.h
|
||||
recv_from_dds_fts.cpp
|
||||
recv_from_dds_fts.h
|
||||
recv_from_tab.h
|
||||
send_to_dds.cpp
|
||||
send_to_dds.h
|
||||
send_to_tab.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
taboc_export.h
|
||||
oc_telegrams.h
|
||||
taboc.dat
|
||||
ttci_channel.cpp
|
||||
ttci_channel.h
|
||||
ttci_device.cpp
|
||||
ttci_device.h
|
||||
|
||||
log4cxx_taboc.props
|
||||
${xml_FILE}
|
||||
)
|
||||
|
||||
# Generate taboc.sql, taboc.xml
|
||||
#CODEGEN_SQL(SRCS_exe ${xml_FILE})
|
||||
|
||||
NGL_ADD_SVC_ICON(SRCS_exe)
|
||||
NGL_ADD_EXECUTABLE(AAR SV svcTrainingAreaBaseOC ${SRCS_exe})
|
||||
NGL_TARGET_LINK_LIBRARIES(svcTrainingAreaBaseOC
|
||||
Qt5::Core
|
||||
Qt5::Network
|
||||
Qt5::SerialPort
|
||||
Qt5::WebSockets
|
||||
LtsCoordTrans
|
||||
LtsSerialPortQuerry
|
||||
LtsCommandLineParser
|
||||
LtsPluginLoader
|
||||
LtsUtilsCore
|
||||
svcCoreClient
|
||||
svcTabocCommon
|
||||
svcTabocClient
|
||||
svcTabocServer
|
||||
svcTabServer
|
||||
svcAesClient
|
||||
svcPdmClient
|
||||
ddsconnector
|
||||
nglsettings
|
||||
ProjectConfig
|
||||
NGLLogger
|
||||
nglcoreapplication
|
||||
ngltools
|
||||
tabTelegrams
|
||||
)
|
||||
NGL_INSTALL_TARGET(svcTrainingAreaBaseOC)
|
||||
NGL_INSTALL(AAR SV FILES log4cxx_taboc.props DESTINATION bin)
|
||||
NGL_INSTALL(AAR SV FILES taboc.dat DESTINATION bin)
|
||||
NGL_ADD_TARGET_LINK(svcTrainingAreaBaseOC sTABOC)
|
||||
|
||||
# --- Executable serialtest -------------------------------------------------
|
||||
|
||||
IF(BUILD_TESTING)
|
||||
SET(TEST_SRC
|
||||
connection_handler.cpp
|
||||
connection_handler.h
|
||||
connection_handler_serial.cpp
|
||||
connection_handler_serial.h
|
||||
connection_handler_tcp_ttci.cpp
|
||||
connection_handler_tcp_ttci.h
|
||||
connection_handler_tcp.cpp
|
||||
connection_handler_tcp.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
testmain.cpp
|
||||
ttci_channel.cpp
|
||||
ttci_channel.h
|
||||
ttci_device.cpp
|
||||
ttci_device.h)
|
||||
|
||||
NGL_ADD_EXECUTABLE(AAR SV serialtest ${TEST_SRC})
|
||||
NGL_TARGET_LINK_LIBRARIES(serialtest
|
||||
Qt5::Core
|
||||
Qt5::SerialPort
|
||||
Qt5::Network
|
||||
LtsSerialPortQuerry
|
||||
svcTabocCommon
|
||||
LtsUtilsCore
|
||||
nglcoreapplication
|
||||
nglsettings
|
||||
ngltools
|
||||
NGLLogger)
|
||||
|
||||
IF(NOT BUILD_RPM)
|
||||
NGL_INSTALL(AAR SV TARGETS serialtest DESTINATION bin)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
+1045
File diff suppressed because it is too large.
Load diff
+220
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Canag Handler for de/encoding LtsUtils::SfmMessage
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-29
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QPair>
|
||||
#include <QTimer>
|
||||
#include <QTcpSocket>
|
||||
|
||||
|
||||
#include <LtsUtilsCore/sfm_bytemessage_converter.h>
|
||||
|
||||
#include "handler_aes_data.h"
|
||||
#include "send_to_tab.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class ConnectionHandler;
|
||||
class MainHandler;
|
||||
class RecvFromTab;
|
||||
|
||||
enum class DdlState
|
||||
{
|
||||
NotStarted = 0,
|
||||
Initiated = 1,
|
||||
Downloading = 2
|
||||
};
|
||||
|
||||
class TxDdlHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TxDdlHandler();
|
||||
|
||||
void addData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void send(const QByteArray &data);
|
||||
|
||||
public slots:
|
||||
void process(const QByteArray &data);
|
||||
|
||||
private slots:
|
||||
void startNextDomainDownload();
|
||||
void sendAbortOrRestartTimeout();
|
||||
|
||||
private:
|
||||
const qint32 s_maxResendCnt = 5;
|
||||
|
||||
struct DdlData
|
||||
{
|
||||
QByteArray data = QByteArray();
|
||||
qint32 offset = 0;
|
||||
};
|
||||
|
||||
void initiateDomainDownload();
|
||||
void domainDownload();
|
||||
void sendAbort(quint8 error = 0);
|
||||
|
||||
DdlState m_state;
|
||||
qint32 m_segmentToggle;
|
||||
qint32 m_sentCnt;
|
||||
QTimer *m_responseTimer;
|
||||
|
||||
QList<DdlData> m_dataToSend;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
class RxDdlHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RxDdlHandler(bool isMultiplexed = false);
|
||||
|
||||
void process(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void send(const QByteArray &data);
|
||||
void received(const QByteArray &data);
|
||||
void receivedMultiplexed(const QByteArray &data, quint32 multiplexer);
|
||||
|
||||
private:
|
||||
void sendDPConfirmation(const quint32 ccs, const quint32 toggle, const quint32 wirkfeldMultiplex = 0);
|
||||
|
||||
bool m_isMultiplexed = false;
|
||||
DdlState m_state = DdlState::NotStarted;
|
||||
|
||||
quint32 m_multiplexer = 0;
|
||||
QByteArray m_messageData;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
class CanagHandler : public QObject
|
||||
, public SendToTab
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CanagHandler(MainHandler *mainHandler);
|
||||
~CanagHandler();
|
||||
|
||||
bool init();
|
||||
|
||||
bool txData(const OcTelegrams::DemandCyclicPositions &data) override;
|
||||
bool txData(const OcTelegrams::TechnicalStateOc &data) override;
|
||||
bool txData(const OcTelegrams::DemandSelftestSfm &data) override;
|
||||
bool txData(const TrainingAreaBase::TetraIcd::OpaquePayloadDataBase &data) override;
|
||||
|
||||
signals:
|
||||
void sendMessage(const LtsUtils::SfmMessage &);
|
||||
public slots:
|
||||
void handleSfmTelegram(const LtsUtils::SfmMessage &message);
|
||||
private slots:
|
||||
void sendDemandDeviceId();
|
||||
void sendDeviceMessage();
|
||||
void sendOcExcon(const QByteArray &data);
|
||||
void sendFireSmokeMarkerExcon(const QByteArray &data);
|
||||
void sendExconOc(const QByteArray &data);
|
||||
void sendExconFireSmokeMarker(const QByteArray &data);
|
||||
void sendSfmAwes(const QByteArray &data);
|
||||
void sendConnectionTestSfm();
|
||||
void receivedExconOc(const QByteArray &data);
|
||||
void receivedExconFireSmokeMarker(const QByteArray &data);
|
||||
void receivedSfmAwes(const QByteArray &data, quint32 multiplexer);
|
||||
void sfmAliveTimeout();
|
||||
void sendFreeText();
|
||||
void sendSyncTime(uint32_t syncTimeSec);
|
||||
|
||||
private:
|
||||
void send(const LtsUtils::SfmMessage &message);
|
||||
void send(quint32 canId, const QByteArray &data);
|
||||
|
||||
void openSocket();
|
||||
|
||||
void readSettings();
|
||||
void fillMessageMap();
|
||||
|
||||
void demandInitialSfmData();
|
||||
void sendDemandTechnicalStateSfm();
|
||||
void sendDemandSpecificsEquipment();
|
||||
void sendDemandSpecificsExercise();
|
||||
|
||||
void handleDisplayMessage(const LtsUtils::SfmMessage &message);
|
||||
|
||||
enum CanId
|
||||
{
|
||||
DemandDeviceId = 1,
|
||||
DeviceId = 2,
|
||||
Time = 7,
|
||||
EnterLeaveAes = 80,
|
||||
ParameterSfmTx = 243,
|
||||
ParameterSfmRx = 244,
|
||||
ParameterOcRx = 263,
|
||||
ParameterOcTx = 264,
|
||||
TechnicalStateSfm = 452,
|
||||
TechnicalStateOc = 466,
|
||||
FireSmokeMarkerExconDataRx = 510,
|
||||
FireSmokeMarkerExconDataTx = 511,
|
||||
ExconFireSmokeMarkerDataRx = 512,
|
||||
ExconFireSmokeMarkerDataTx = 513,
|
||||
ExconOcDataRx = 520,
|
||||
ExconOcDataTx = 521,
|
||||
OcExconDataRx = 522,
|
||||
OcExconDataTx = 523,
|
||||
DemandAwesData = 530,
|
||||
AwesDataRx = 531,
|
||||
AwesDataTx = 532,
|
||||
Position = 540,
|
||||
DemandCyclicPosition = 545,
|
||||
DisplayMessage = 684,
|
||||
SpecificsExercise = 732,
|
||||
SpecificsOc = 738,
|
||||
SpecificsEquipment = 742,
|
||||
EquipmentId = 765,
|
||||
ConnectionTestSfm = 1101,
|
||||
ConnectionTestSfmReply = 1102,
|
||||
WakeUp = 1760,
|
||||
WakeUpSfm = 1773
|
||||
};
|
||||
|
||||
MainHandler *m_mainHandler;
|
||||
std::shared_ptr<ConnectionHandler> m_connectionHandler;
|
||||
std::shared_ptr<HandlerAesData> m_handlerAesData;
|
||||
std::shared_ptr<RecvFromTab> m_rxHandler;
|
||||
|
||||
QByteArray m_freeText;
|
||||
|
||||
QTimer *m_deviceMessageTimer;
|
||||
QTimer *m_freeTextCollectionTimer;
|
||||
QTimer *m_sfmAliveTimer;
|
||||
QTimer *m_sfmConnectionCheckTimer;
|
||||
|
||||
QTcpSocket *m_syncSocket;
|
||||
|
||||
OcTelegrams::TechnicalStateSfm m_technicalStateSfm; // not just the telegram technicalState of the sfm but also total sfm connection check
|
||||
bool m_sfmConnectionEstablished;
|
||||
|
||||
QMap<CanId, LtsUtils::SfmMessage *> m_messages;
|
||||
QMap<CanId, TxDdlHandler *> m_txDdlHandler;
|
||||
QMap<CanId, RxDdlHandler *> m_rxDdlHandler;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Connection Handler for the serial connection to the sfm
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-29
|
||||
*/
|
||||
|
||||
#include "connection_handler.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
#ifdef GUEZ_MODE
|
||||
ConnectionHandler::ConnectionHandler()
|
||||
: ConnectionHandlerSerial()
|
||||
{}
|
||||
#else
|
||||
ConnectionHandler::ConnectionHandler()
|
||||
: ConnectionHandlerTcpTtci()
|
||||
{}
|
||||
#endif
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Connection Handler for the serial connection to the sfm
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-29
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "connection_handler_tcp.h"
|
||||
#include "connection_handler_tcp_ttci.h"
|
||||
#include "connection_handler_serial.h"
|
||||
|
||||
//TODO: Interface me!
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
#ifdef GUEZ_MODE
|
||||
class ConnectionHandler : public ConnectionHandlerSerial
|
||||
#else
|
||||
class ConnectionHandler : public ConnectionHandlerTcpTtci
|
||||
#endif
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConnectionHandler();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Connection Handler for the serial connection to the sfm
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-29
|
||||
*/
|
||||
|
||||
#include "connection_handler_serial.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
ConnectionHandlerSerial::ConnectionHandlerSerial()
|
||||
: m_spQuerry(new LtsSerialPortQuerry::LtsSerialPortQuerry(this))
|
||||
, m_receiveTimeoutTimer(new QTimer(this))
|
||||
, m_logger(getLogger("ConnectionHandlerSerial"))
|
||||
{
|
||||
connect(m_spQuerry, &LtsSerialPortQuerry::LtsSerialPortQuerry::connectionEstablished, this, &ConnectionHandlerSerial::open);
|
||||
m_spQuerry->restartQuerry();
|
||||
m_receiveTimeoutTimer->setInterval(5000);
|
||||
connect(m_receiveTimeoutTimer, &QTimer::timeout, m_spQuerry, &LtsSerialPortQuerry::LtsSerialPortQuerry::continueQuerry);
|
||||
connect(m_receiveTimeoutTimer, &QTimer::timeout, this, &ConnectionHandlerSerial::disconnectedFromEq);
|
||||
m_receiveTimeoutTimer->start();
|
||||
}
|
||||
|
||||
bool ConnectionHandlerSerial::init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectionHandlerSerial::send(const LtsUtils::SfmMessage &message)
|
||||
{
|
||||
if (!m_serialPort)
|
||||
return false;
|
||||
|
||||
if (!m_serialPort->isOpen())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Port is not opened!";
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data = LtsUtils::byteStreamFromSfmMessage(message);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Sending frame: " << data.toHex(' ');
|
||||
|
||||
const qint64 byteswritten = m_serialPort->write(data);
|
||||
m_serialPort->flush();
|
||||
|
||||
if (byteswritten == -1)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to write data to port " << m_serialPort->portName() << " error: " << m_serialPort->errorString();
|
||||
return false;
|
||||
}
|
||||
else if (byteswritten != data.size())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to write all data to port " << m_serialPort->portName() << " error: " << m_serialPort->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectionHandlerSerial::open()
|
||||
{
|
||||
m_serialPort = m_spQuerry->currentSerialPort();
|
||||
m_currentDataRead.clear();
|
||||
|
||||
NOTICE_LOG(m_logger) << "(Re)openend serial port " << m_serialPort->portName() << " baudrate: " << m_serialPort->baudRate();
|
||||
connect(m_serialPort, &QSerialPort::readyRead, this, &ConnectionHandlerSerial::read);
|
||||
m_receiveTimeoutTimer->start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectionHandlerSerial::isOpen() const
|
||||
{
|
||||
return m_serialPort && m_serialPort->isOpen();
|
||||
}
|
||||
|
||||
void ConnectionHandlerSerial::read()
|
||||
{
|
||||
if (!m_serialPort)
|
||||
return;
|
||||
const QByteArray ba = m_serialPort->readAll();
|
||||
m_currentDataRead.append(ba);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Binary data from sfm: " << ba.toHex(' ');
|
||||
|
||||
int idxStart = -1;
|
||||
int idxEnd = -1;
|
||||
|
||||
while ((idxStart = m_currentDataRead.indexOf(0x02)) >= 0 &&
|
||||
(idxEnd = m_currentDataRead.indexOf(0x03)) >= 0) // while complete message frame in buffer
|
||||
{
|
||||
QByteArray message = m_currentDataRead.mid(idxStart, (idxEnd - idxStart) + 1);
|
||||
|
||||
LtsUtils::SfmMessage sfmMsg;
|
||||
DEBUG_LOG(m_logger) << "Frame from sfm as binary " << message.toHex();
|
||||
try
|
||||
{
|
||||
sfmMsg = LtsUtils::byteStreamToSfmMessage(message);
|
||||
}
|
||||
catch (const LtsUtils::ConversionError &ex)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error converting QByteArray into LtsUtils::SfmMessage: " << ex.what();
|
||||
ERROR_LOG(m_logger) << "Faulty Data from sfm as binary " << message.toHex();
|
||||
m_currentDataRead = m_currentDataRead.mid(idxEnd + 1);
|
||||
//we continue but dont restart the timer
|
||||
continue;
|
||||
}
|
||||
|
||||
INFO_LOG(m_logger) << "Decoded message from sfm Id: " << sfmMsg.id() << " RTR: " << sfmMsg.rtr() << " Data: " << sfmMsg.payload().toHex();
|
||||
// start new interval
|
||||
m_receiveTimeoutTimer->start();
|
||||
|
||||
m_currentDataRead = m_currentDataRead.mid(idxEnd + 1);
|
||||
|
||||
emit received(sfmMsg);
|
||||
}
|
||||
|
||||
if (m_serialPort->bytesAvailable() > 0)
|
||||
DEBUG_LOG(m_logger) << "Still " << m_serialPort->bytesAvailable() << " available to be read";
|
||||
}
|
||||
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Connection Handler for the serial connection to the sfm
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-29
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QtSerialPort/QSerialPort>
|
||||
|
||||
#include <LtsSerialPortQuerry/lts_serial_port_querry.h>
|
||||
|
||||
#include <LtsUtilsCore/sfm_bytemessage_converter.h>
|
||||
#include "logger.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
class ConnectionHandlerSerial : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ConnectionHandlerSerial();
|
||||
|
||||
bool init();
|
||||
bool send(const LtsUtils::SfmMessage &message);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool isOpen() const;
|
||||
|
||||
signals:
|
||||
void received(const LtsUtils::SfmMessage &message);
|
||||
void disconnectedFromEq();
|
||||
|
||||
private slots:
|
||||
void read();
|
||||
|
||||
private:
|
||||
LtsSerialPortQuerry::LtsSerialPortQuerry *m_spQuerry = nullptr;
|
||||
QSerialPort *m_serialPort = nullptr;
|
||||
QTimer *m_receiveTimeoutTimer = nullptr;
|
||||
QByteArray m_currentDataRead;
|
||||
int m_errCnt = 0;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Connection Handler for the serial connection to the sfm
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-29
|
||||
*/
|
||||
|
||||
#include "connection_handler_tcp.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
ConnectionHandlerTcp::ConnectionHandlerTcp()
|
||||
: m_timer(new QTimer(this))
|
||||
, m_logger(getLogger("ConnectionHandlerTcp"))
|
||||
{
|
||||
m_timer->setInterval(5000);
|
||||
connect(m_timer, &QTimer::timeout, this, &ConnectionHandlerTcp::open);
|
||||
}
|
||||
|
||||
ConnectionHandlerTcp::~ConnectionHandlerTcp()
|
||||
{
|
||||
m_socket->close();
|
||||
|
||||
delete m_socket;
|
||||
}
|
||||
|
||||
bool ConnectionHandlerTcp::init()
|
||||
{
|
||||
m_socket = new QTcpSocket();
|
||||
|
||||
connect(m_socket, &QTcpSocket::readyRead, this, &ConnectionHandlerTcp::read);
|
||||
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &ConnectionHandlerTcp::handleError);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &ConnectionHandlerTcp::open);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &ConnectionHandlerTcp::disconnectedFromEq);
|
||||
open();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectionHandlerTcp::send(const LtsUtils::SfmMessage &message)
|
||||
{
|
||||
if (!m_socket)
|
||||
return false;
|
||||
|
||||
if (!m_socket->isOpen())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Port is not opened!";
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray data = LtsUtils::byteStreamFromSfmMessage(message);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Sending frame: " << data.toHex(' ');
|
||||
|
||||
const qint64 byteswritten = m_socket->write(data);
|
||||
|
||||
if (byteswritten == -1)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to write data to port " << m_socket->peerPort() << " error: " << m_socket->errorString();
|
||||
m_timer->start();
|
||||
return false;
|
||||
}
|
||||
else if (byteswritten != data.size())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Failed to write all data to port " << m_socket->peerPort() << " error: " << m_socket->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectionHandlerTcp::open()
|
||||
{
|
||||
m_timer->stop();
|
||||
m_socket->connectToHost(Settings::hostName(), Settings::port());
|
||||
if (!m_socket->waitForConnected())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not (re)open tcp destination " << m_socket->peerName() << ":" << m_socket->peerPort() << ", error: " << m_socket->errorString();
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(m_logger) << "(Re)openend tcp destination " << m_socket->peerName() << ":" << Settings::port();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionHandlerTcp::close()
|
||||
{
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
bool ConnectionHandlerTcp::isOpen() const
|
||||
{
|
||||
return m_socket->isOpen();
|
||||
}
|
||||
|
||||
void ConnectionHandlerTcp::read()
|
||||
{
|
||||
const QByteArray ba = m_socket->readAll();
|
||||
m_currentDataRead.append(ba);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Binary data from sfm: " << ba.toHex(' ');
|
||||
|
||||
int idxStart = -1;
|
||||
int idxEnd = -1;
|
||||
while ((idxStart = m_currentDataRead.indexOf(0x02)) >= 0 &&
|
||||
(idxEnd = m_currentDataRead.indexOf(0x03)) >= 0) // while complete message frame in buffer
|
||||
{
|
||||
QByteArray message = m_currentDataRead.mid(idxStart, (idxEnd - idxStart) + 1);
|
||||
|
||||
LtsUtils::SfmMessage sfmMsg;
|
||||
DEBUG_LOG(m_logger) << "Frame from sfm as binary " << message.toHex();
|
||||
|
||||
try
|
||||
{
|
||||
sfmMsg = LtsUtils::byteStreamToSfmMessage(message);
|
||||
}
|
||||
catch (const LtsUtils::ConversionError &ex)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error converting QByteArray into LtsUtils::SfmMessage: " << ex.what();
|
||||
ERROR_LOG(m_logger) << "Faulty Data from sfm as binary " << message.toHex();
|
||||
}
|
||||
|
||||
INFO_LOG(m_logger) << "Decoded message from sfm Id: " << sfmMsg.id() << " RTR: " << sfmMsg.rtr() << " Data: " << sfmMsg.payload().toHex();
|
||||
|
||||
m_currentDataRead = m_currentDataRead.mid(idxEnd + 1);
|
||||
|
||||
emit received(sfmMsg);
|
||||
}
|
||||
|
||||
if (m_socket->bytesAvailable() > 0)
|
||||
DEBUG_LOG(m_logger) << "Still " << m_socket->bytesAvailable() << " available to be read";
|
||||
}
|
||||
|
||||
void ConnectionHandlerTcp::handleError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Spontaneous error " << error << " doesn't have to be an error ...";
|
||||
|
||||
const QString errStr(QStringLiteral("QTcpSocket::SocketError ") + QString::number(error) +
|
||||
QStringLiteral(" from tcp port ") + QString::number(m_socket->peerPort()) +
|
||||
QStringLiteral(" error: ") + m_socket->errorString());
|
||||
|
||||
switch (error)
|
||||
{
|
||||
case QTcpSocket::SocketError::ConnectionRefusedError:
|
||||
case QTcpSocket::SocketError::SocketAccessError:
|
||||
case QTcpSocket::SocketError::SocketResourceError:
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Closing port trying to reopen it.";
|
||||
close();
|
||||
m_timer->start();
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
case QTcpSocket::SocketError::RemoteHostClosedError:
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Host closed trying to reopen.";
|
||||
m_timer->start();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
ERROR_LOG(m_logger) << errStr;
|
||||
close();
|
||||
m_timer->start();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Connection Handler for the serial connection to the sfm
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-29
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include <LtsUtilsCore/sfm_bytemessage_converter.h>
|
||||
#include "logger.h"
|
||||
|
||||
class QTimer;
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
class ConnectionHandlerTcp : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConnectionHandlerTcp();
|
||||
~ConnectionHandlerTcp();
|
||||
|
||||
bool init();
|
||||
bool send(const LtsUtils::SfmMessage &message);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool isOpen() const;
|
||||
|
||||
signals:
|
||||
void received(const LtsUtils::SfmMessage &message);
|
||||
void disconnectedFromEq();
|
||||
|
||||
|
||||
private slots:
|
||||
void read();
|
||||
void handleError(QAbstractSocket::SocketError error);
|
||||
|
||||
private:
|
||||
QTcpSocket *m_socket = nullptr;
|
||||
QTimer *m_timer = nullptr;
|
||||
QByteArray m_currentDataRead;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Holen und Schreiben der XDR-Telegramme vom/an Can-Bus
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#include "connection_handler_tcp_ttci.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDataStream>
|
||||
#include <QTimer>
|
||||
#include <QTcpServer>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
using namespace NGL;
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
ConnectionHandlerTcpTtci::ConnectionHandlerTcpTtci()
|
||||
: m_messageDataStart(true)
|
||||
, m_wirkfeldDataStart(true)
|
||||
, m_wirkfeldMultiplex(0)
|
||||
, m_sendingData(false)
|
||||
, m_segmentToggle(0)
|
||||
, m_canDevice(new Device(this))
|
||||
, m_canChannel(new Channel(QLatin1Char('@'), true, this))
|
||||
, m_logger(getLogger("TTCI"))
|
||||
{
|
||||
connect(m_canChannel, &Channel::dataAvailable, this, &ConnectionHandlerTcpTtci::slotReadCanFrame);
|
||||
connect(m_canChannel, &Channel::disconnectedFromEq, this, &ConnectionHandlerTcpTtci::disconnectedFromEq);
|
||||
}
|
||||
|
||||
ConnectionHandlerTcpTtci::~ConnectionHandlerTcpTtci()
|
||||
{
|
||||
delete m_canChannel;
|
||||
delete m_canDevice;
|
||||
qDeleteAll(m_sfmMessages);
|
||||
}
|
||||
|
||||
bool ConnectionHandlerTcpTtci::init()
|
||||
{
|
||||
initCanSocket();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ConnectionHandlerTcpTtci::initCanSocket()
|
||||
{
|
||||
ERROR_LOG(m_logger) << "InitCantSOcket";
|
||||
|
||||
m_canChannel->setDevice(m_canDevice);
|
||||
m_canDevice->open();
|
||||
}
|
||||
|
||||
|
||||
bool ConnectionHandlerTcpTtci::findNextSyncByte(int &iCurReadPos) const
|
||||
{
|
||||
if (iCurReadPos == m_buffer.size())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "No more data available";
|
||||
return false;
|
||||
}
|
||||
DEBUG_LOG(m_logger) << "Searching for next sync byte, start at: " << iCurReadPos;
|
||||
NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Debug, m_buffer);
|
||||
|
||||
while (iCurReadPos < m_buffer.size())
|
||||
{
|
||||
static const unsigned char syncByte = 0xAA;
|
||||
const unsigned char curByte = m_buffer.at(iCurReadPos);
|
||||
if (curByte == syncByte)
|
||||
break;
|
||||
++iCurReadPos;
|
||||
}
|
||||
// wir brauchen mind. 4 Bytes (Sync, Can Header1+2, CRC)
|
||||
if (m_buffer.size() < 4 + iCurReadPos)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Could not find sync byte in stream";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionHandlerTcpTtci::slotReadCanFrame(const QByteArray &data)
|
||||
{
|
||||
m_buffer += data;
|
||||
|
||||
int iCurReadPos = 0;
|
||||
while (findNextSyncByte(iCurReadPos))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Found Sync byte at " << iCurReadPos;
|
||||
|
||||
// die ersten 4 Bits sind die Datenlänge
|
||||
unsigned int len = (m_buffer.at(iCurReadPos + 1) & 0xF0) >> 4;
|
||||
DEBUG_LOG(m_logger) << "Payload size: " << len;
|
||||
|
||||
if (len > 8)
|
||||
{
|
||||
// spezielle Daten
|
||||
switch (len)
|
||||
{
|
||||
case 9:
|
||||
// Status-Informationen (3.3.1.2)
|
||||
DEBUG_LOG(m_logger) << "Read status data - skipping ";
|
||||
iCurReadPos += 4;
|
||||
break;
|
||||
case 11:
|
||||
// Komm SFM - PAN (3.3.2)
|
||||
DEBUG_LOG(m_logger) << "Read communication SFM - PAN - skipping ";
|
||||
iCurReadPos += 5;
|
||||
break;
|
||||
case 12:
|
||||
// Parameterabfrage (3.3.1.3)
|
||||
DEBUG_LOG(m_logger) << "Read parameter request - skipping ";
|
||||
iCurReadPos += 5;
|
||||
break;
|
||||
default:
|
||||
// wahrscheinlich kein korrekter Sync
|
||||
WARNING_LOG(m_logger) << "Invalid payload size (" << len << " - skipping";
|
||||
++iCurReadPos;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 1 Byte sync
|
||||
// 2 Byte header
|
||||
// len Byte nutzdaten
|
||||
// 1 Byte crc
|
||||
int neededBytes = 4 + len + iCurReadPos;
|
||||
if (m_buffer.size() < neededBytes)
|
||||
{
|
||||
INFO_LOG(m_logger) << "To few bytes in buffer - waiting for more (want: "
|
||||
<< neededBytes << " have: " << m_buffer.size() << ")";
|
||||
// zu wenig Daten -> warten
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int id = (m_buffer.at(iCurReadPos + 1) & 0x07);
|
||||
unsigned char id2 = m_buffer.at(iCurReadPos + 2);
|
||||
id = ((id << 8) + id2);
|
||||
bool rtr = ((m_buffer.at(iCurReadPos + 1) & 0x08) == 0x08);
|
||||
DEBUG_LOG(m_logger) << "CAN ID: " << id << " rtr: " << rtr;
|
||||
|
||||
// calc crc (über alles bis zur CRC)
|
||||
unsigned char crc = 0;
|
||||
for (unsigned int i = 0; i < len + 3; ++i)
|
||||
crc += m_buffer.at(iCurReadPos + i);
|
||||
crc = ~crc;
|
||||
|
||||
unsigned char crcSent = m_buffer.at(iCurReadPos + len + 3);
|
||||
if (crc != crcSent)
|
||||
{
|
||||
// CRC passt nicht - ggf. kein korrekter Sync
|
||||
WARNING_LOG(m_logger) << "CRC checksum missmatch (is: " << crcSent
|
||||
<< " - calculated: " << crc << ") - skipping";
|
||||
iCurReadPos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
QByteArray data2(m_buffer.data() + iCurReadPos + 3, len);
|
||||
//LtsUtils::SfmMessage msg;
|
||||
LtsUtils::SfmMessage msg;
|
||||
msg.setId(id);
|
||||
msg.setPayload(data2);
|
||||
msg.setRtr(rtr);
|
||||
|
||||
emit received(msg);
|
||||
|
||||
// und weiter zum nächsten Paket
|
||||
iCurReadPos += 4 + len;
|
||||
|
||||
}
|
||||
m_buffer = m_buffer.mid(iCurReadPos);
|
||||
|
||||
}
|
||||
|
||||
bool ConnectionHandlerTcpTtci::isOpen()
|
||||
{
|
||||
return m_canDevice->isOpen();
|
||||
}
|
||||
|
||||
bool ConnectionHandlerTcpTtci::send(const LtsUtils::SfmMessage &message)
|
||||
{
|
||||
if (message.payload().size() > 8)
|
||||
return false;
|
||||
|
||||
INFO_LOG(m_logger) << "start write";
|
||||
NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Debug, message.payload());
|
||||
|
||||
QByteArray data(message.payload().size() + 4, Qt::Uninitialized);
|
||||
data[0] = 0xAA;
|
||||
|
||||
data[1] = (message.payload().size() << 4) | ((message.rtr() & 0x1) << 3) | ((message.id() & 0x700) >> 8);
|
||||
data[2] = (message.id() & 0xFF);
|
||||
|
||||
for (int i = 0; i < message.payload().size(); ++i)
|
||||
data[i + 3] = message.payload().at(i);
|
||||
|
||||
unsigned char crc = 0;
|
||||
for (int i = 0; i < data.size() - 1; ++i)
|
||||
crc += data.at(i);
|
||||
crc = ~crc;
|
||||
data[message.payload().size() + 3] = crc;
|
||||
INFO_LOG(m_logger) << "end write ";
|
||||
|
||||
if (!m_canChannel->writeData(data))
|
||||
ERROR_LOG(m_logger) << "Error writing data to can channel";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Holen und Schreiben der XDR-Telegramme vom/an Can-Bus
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QSettings>
|
||||
|
||||
#include <LtsUtilsCore/sfm_bytemessage_converter.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "ttci_channel.h"
|
||||
#include "ttci_device.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
|
||||
class ConnectionHandlerTcpTtci : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConnectionHandlerTcpTtci();
|
||||
~ConnectionHandlerTcpTtci();
|
||||
|
||||
bool init();
|
||||
|
||||
|
||||
bool send(const LtsUtils::SfmMessage &message);
|
||||
bool isOpen();
|
||||
|
||||
signals:
|
||||
void dataSent();
|
||||
void received(const LtsUtils::SfmMessage &message);
|
||||
void disconnectedFromEq();
|
||||
|
||||
private:
|
||||
void initCanSocket();
|
||||
void newDataAvailable(const QByteArray &);
|
||||
bool findNextSyncByte(int &iCurReadPos) const;
|
||||
|
||||
private slots:
|
||||
void slotReadCanFrame(const QByteArray &data);
|
||||
|
||||
|
||||
private:
|
||||
QByteArray m_messageData;
|
||||
bool m_messageDataStart;
|
||||
QByteArray m_wirkfeldData;
|
||||
bool m_wirkfeldDataStart;
|
||||
quint32 m_wirkfeldMultiplex;
|
||||
QVector<LtsUtils::SfmMessage *> m_sfmMessages;
|
||||
bool m_sendingData;
|
||||
qint32 m_segmentToggle;
|
||||
QList<QPair<qint32, QByteArray> > m_dataToSend;
|
||||
Device *m_canDevice;
|
||||
Channel *m_canChannel;
|
||||
QByteArray m_buffer;
|
||||
LOGGER_T m_logger;
|
||||
|
||||
};
|
||||
|
||||
} //namespace
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* DdsHandler for DDS
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
|
||||
#include "dds_handler.h"
|
||||
|
||||
#include <DDS/Core/pacemaker.h>
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
DdsHandler::DdsHandler(int domainId, int exerciseId)
|
||||
: m_ddsConnector(new DDSConnector::DdsConnector(domainId, IDL::PartitionType::Exercise, 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();
|
||||
|
||||
m_dtcCore = Core::Interface::Dtc::create(m_ddsConnector);
|
||||
bool bRet = m_dtcCore->enablePartitionTime();
|
||||
|
||||
// Pacemaker
|
||||
m_pacemaker = std::make_shared<Core::Pacemaker>(m_ddsConnector);
|
||||
|
||||
m_dtsTaboc = Interface::Dts::create(m_ddsConnector);
|
||||
bRet &= m_dtsTaboc->enableAll();
|
||||
INFO_LOG(m_logger) << "Dts TrainingAreaBaseOc initialised";
|
||||
|
||||
m_dtcTaboc = Interface::Dtc::create(m_ddsConnector);
|
||||
bRet &= m_dtsTaboc->enableSpecificsEquipment();
|
||||
INFO_LOG(m_logger) << "Dtc TrainingAreaBaseOc initialised";
|
||||
|
||||
m_ftsTaboc = Interface::Fts::create(m_ddsConnector);
|
||||
bRet &= m_ftsTaboc->enableAll();
|
||||
INFO_LOG(m_logger) << "Fts TrainingAreaBaseOc initialised";
|
||||
|
||||
m_dtsTab = TrainingAreaBase::Interface::Dts::create(m_ddsConnector);
|
||||
bRet &= (m_dtsTab->enableOcData() && m_dtsTab->enableFireSmokeMarkerData());
|
||||
INFO_LOG(m_logger) << "Dts TrainingAreaBase initialised";
|
||||
|
||||
m_ftsTab = TrainingAreaBase::Interface::Fts::create(m_ddsConnector);
|
||||
bRet &= (m_ftsTab->enableSendOcData() && m_ftsTab->enableSendFireSmokeMarkerData());
|
||||
INFO_LOG(m_logger) << "Fts TrainingAreaBase initialised";
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
qint64 DdsHandler::currentPartitionTime() const
|
||||
{
|
||||
return m_dtcCore->partitionTime().timeStamp();
|
||||
}
|
||||
|
||||
} // Namespace PDM
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* DdsHandler for DDS
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <DDS/DDSConnector/dds_connector.h>
|
||||
#include <DDS/Core/core_dtc.h>
|
||||
|
||||
#include <TrainingAreaBaseOC/taboc_dts.h>
|
||||
#include <TrainingAreaBaseOC/taboc_fts.h>
|
||||
#include <TrainingAreaBaseOC/taboc_dtc.h>
|
||||
#include <TrainingAreaBase/tab_dts.h>
|
||||
#include <TrainingAreaBase/tab_fts.h>
|
||||
#include "logger.h"
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class Pacemaker;
|
||||
}
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
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<Interface::Dts> dtsTaboc() const
|
||||
{
|
||||
return m_dtsTaboc;
|
||||
}
|
||||
|
||||
std::shared_ptr<Interface::Dtc> dtcTaboc() const
|
||||
{
|
||||
return m_dtcTaboc;
|
||||
}
|
||||
|
||||
std::shared_ptr<Interface::Fts> ftsTaboc() const
|
||||
{
|
||||
return m_ftsTaboc;
|
||||
}
|
||||
std::shared_ptr<TrainingAreaBase::Interface::Dts> dtsTab() const
|
||||
{
|
||||
return m_dtsTab;
|
||||
}
|
||||
|
||||
std::shared_ptr<TrainingAreaBase::Interface::Fts> ftsTab() const
|
||||
{
|
||||
return m_ftsTab;
|
||||
}
|
||||
|
||||
std::shared_ptr<DDSConnector::DdsConnector> ddsConnector() const
|
||||
{
|
||||
return m_ddsConnector;
|
||||
}
|
||||
|
||||
qint64 currentPartitionTime() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
|
||||
std::shared_ptr<Core::Pacemaker> m_pacemaker;
|
||||
std::shared_ptr<Core::Interface::Dtc> m_dtcCore;
|
||||
std::shared_ptr<Interface::Dts> m_dtsTaboc;
|
||||
std::shared_ptr<Interface::Dtc> m_dtcTaboc;
|
||||
std::shared_ptr<Interface::Fts> m_ftsTaboc;
|
||||
std::shared_ptr<TrainingAreaBase::Interface::Dts> m_dtsTab;
|
||||
std::shared_ptr<TrainingAreaBase::Interface::Fts> m_ftsTab;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
+803
@@ -0,0 +1,803 @@
|
||||
/**
|
||||
* Handler for AES data received from SFM
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-04-11
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QPointF>
|
||||
|
||||
#include <LtsUtilsCore/qbytearray_byteaccess.h>
|
||||
|
||||
#include "handler_aes_data.h"
|
||||
#include "settings.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
// mathematical positive north -> mathematical negativ, east = 0°, 0,01 degree in 1/100° -> factor 100
|
||||
static inline int compassToMathAngleForSfm(double compassDegree)
|
||||
{
|
||||
int ret = qRound((90.0 - compassDegree / 100.0));
|
||||
while (ret < 0)
|
||||
ret += 360;
|
||||
while (ret > 360)
|
||||
ret -= 360;
|
||||
return ret;
|
||||
}
|
||||
|
||||
HandlerAesData::HandlerAesData(const std::shared_ptr<DdsHandler> &dds,
|
||||
const std::shared_ptr<AreaEffectSimulation::Interface::Dtc> &aesDtc,
|
||||
const std::shared_ptr<AreaEffectSimulation::Interface::Ftc> &aesFtc,
|
||||
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc)
|
||||
: m_dds(dds)
|
||||
, m_aesDtc(aesDtc)
|
||||
, m_aesFtc(aesFtc)
|
||||
, m_pdmDtc(pdmDtc)
|
||||
, m_utmCoord(LtsCoordTrans::UtmCoordinate::fromMgrs(Settings::baseCoord()))
|
||||
, m_logger(getLogger("HandlerAesData"))
|
||||
{
|
||||
}
|
||||
|
||||
bool HandlerAesData::init()
|
||||
{
|
||||
// Enable AES DTC
|
||||
if (!m_aesDtc->enableBCContamination() || !m_aesDtc->enableExplosionArea() || !m_aesDtc->enableExplosionSingle() || !m_aesDtc->enableFunctionalArea()
|
||||
|| !m_aesDtc->enableNContamination() || !m_aesDtc->enableMineArea() || !m_aesDtc->enableExplosionCluster())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error enabling AES Data-Topics.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable AES FTC
|
||||
if (!m_aesFtc->enableBCContaminationModify() || !m_aesFtc->enableExplosionAreaModify() || !m_aesFtc->enableExplosionSingleModify()
|
||||
|| !m_aesFtc->enableFunctionalAreaModify() || !m_aesFtc->enableNContaminationModify() || !m_aesFtc->enableExplosionClusterOcModify()
|
||||
|| !m_aesFtc->enableMineAreaModifyFromCtaData() || !m_aesFtc->enableBCContaminationRemove() || !m_aesFtc->enableExplosionAreaRemove()
|
||||
|| !m_aesFtc->enableExplosionSingleRemove() || !m_aesFtc->enableFunctionalAreaRemove() || !m_aesFtc->enableNContaminationRemove()
|
||||
|| !m_aesFtc->enableMineAreaRemove() || !m_aesFtc->enableExplosionClusterRemove() || !m_aesFtc->enableExplosionAreaPauseResume()
|
||||
|| !m_aesFtc->enableMineAreaReferenceModify())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error enabling AES Function-Topics.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable PDM DTC
|
||||
|
||||
if (!m_pdmDtc->enableChargeType() || !m_pdmDtc->enableGearTypeChargeType() || !m_pdmDtc->enableGearType())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error enabling PDM Data-Topic.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_aesDtc->waitForHistoricalDataBCContamination(10000) || !m_aesDtc->waitForHistoricalDataExplosionArea(10000)
|
||||
|| !m_aesDtc->waitForHistoricalDataExplosionSingle(10000) || !m_aesDtc->waitForHistoricalDataFunctionalArea(10000)
|
||||
|| !m_aesDtc->waitForHistoricalDataNContamination(10000) || !m_aesDtc->waitForHistoricalDataMineArea(10000)
|
||||
|| !m_aesDtc->waitForHistoricalDataExplosionCluster(10000))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error waiting for historical data AES.";
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "Initialisation successful";
|
||||
return true;
|
||||
}
|
||||
|
||||
void HandlerAesData::process(const QByteArray &data, int multiplexer)
|
||||
{
|
||||
if (data.isEmpty())
|
||||
return;
|
||||
switch (multiplexer)
|
||||
{
|
||||
case MineArea:
|
||||
{
|
||||
mineAreas(data);
|
||||
break;
|
||||
}
|
||||
case ExplosionArea:
|
||||
{
|
||||
explosionAreas(data);
|
||||
break;
|
||||
}
|
||||
case ExplosionSingle:
|
||||
{
|
||||
explosionSingles(data);
|
||||
break;
|
||||
}
|
||||
case NContamination:
|
||||
{
|
||||
nContaminations(data);
|
||||
break;
|
||||
}
|
||||
case BcContamination:
|
||||
{
|
||||
bcContaminations(data);
|
||||
break;
|
||||
}
|
||||
case FunctionalArea:
|
||||
{
|
||||
functionalAreas(data);
|
||||
break;
|
||||
}
|
||||
case ExplosionAreaStop:
|
||||
{
|
||||
explosionAreasPause(data);
|
||||
break;
|
||||
}
|
||||
case ExplosionAreaContinue:
|
||||
{
|
||||
explosionAreasResume(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WARNING_LOG(m_logger) << "Got unknown multiplexer.";
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::specificsExercise(const OcTelegrams::SpecificsExercise &data)
|
||||
{
|
||||
m_utmCoord = LtsCoordTrans::UtmCoordinate::fromMgrs(QString::number(data.zone) + QLatin1Char(data.band) + QString::fromLatin1(data.square)
|
||||
+ QString::number(data.northing).rightJustified(7, QLatin1Char('0'))
|
||||
+ QString::number(data.easting).rightJustified(7, QLatin1Char('0')));
|
||||
}
|
||||
|
||||
void HandlerAesData::onMineAreaModifyFromCtaDataReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Mine area modify failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Mine area modify successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onNContaminationModifyReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "N contamination modify failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "N contamination modify successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onFunctionalAreaModifyReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Functional area modify failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Functional area modify successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onExplosionSingleModifyReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Explosion single modify failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Explosion single modify successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onExplosionAreaModifyReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Explosion area modify failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Explosion area modify successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onBCContaminationModifyReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "BC contamination modify failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "BC contamination modify successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onExplosionClusterOcModifyReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "ExplosionCluster modify failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "ExplosionCluster successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onMineAreaRemoveReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Mine area remove failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Mine area remove successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onNContaminationRemoveReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "N contamination remove failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "N contamination remove successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onFunctionalAreaRemoveReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Functional area remove failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Functional area remove successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onExplosionSingleRemoveReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Explosion single remove failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Explosion single remove successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onExplosionAreaRemoveReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Explosion area remove failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Explosion area remove successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onBCContaminationRemoveReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "BC contamination remove failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "BC contamination remove successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onExplosionClusterRemoveReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "ExplosionCluster remove failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "ExplosionCluster remove successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
void HandlerAesData::onExplosionAreaPauseResumeReply(const Core::FunctionReplyObject &reply)
|
||||
{
|
||||
if (reply.error())
|
||||
ERROR_LOG(m_logger) << "Explosion area pause/resume failed: " << reply.translated();
|
||||
else
|
||||
DEBUG_LOG(m_logger) << "Explosion area pause/resume successfull. Id: " << reply.id();
|
||||
}
|
||||
|
||||
template <typename T> static inline void getBasicData(const QByteArray &data, size_t &pos, T &req, bool withoutEffectStart = false)
|
||||
{
|
||||
uint64_t tmp;
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.id(tmp);
|
||||
req.name(data.mid(pos, 8).toStdString());
|
||||
QString name = QString::fromStdString(req.name());
|
||||
|
||||
// Linux doesn't like it when there are non plausable charaters
|
||||
name = name.replace(QRegExp(QStringLiteral("[^ -~]")), QLatin1String(""));
|
||||
name = name.trimmed();
|
||||
if (!name.isEmpty())
|
||||
req.name(name.toStdString());
|
||||
|
||||
pos += 8;
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
if (tmp != 0)
|
||||
req.partyId(dds::core::optional<int32_t>(tmp));
|
||||
else
|
||||
req.partyId(dds::core::optional<int32_t>());
|
||||
if (!withoutEffectStart)
|
||||
{
|
||||
LtsUtils::get4Byte(data, pos, tmp);
|
||||
if (tmp != 0)
|
||||
req.effectStart(dds::core::optional<rti::core::int64>(tmp * 1000));
|
||||
else
|
||||
req.effectStart(dds::core::optional<rti::core::int64>());
|
||||
}
|
||||
}
|
||||
// mathematical negativ east = 0° -> mathematical positiv, north = 0°, 1/100° as int -> factor 100
|
||||
static inline double mathToCompassAngle(int mathDegree)
|
||||
{
|
||||
int ret = 9000 - mathDegree;
|
||||
while (ret < 0)
|
||||
ret += 36000;
|
||||
while (ret > 36000)
|
||||
ret -= 36000;
|
||||
|
||||
return ret / 100.0;
|
||||
}
|
||||
|
||||
void HandlerAesData::bcContaminations(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got new BC contamination data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
QList<int> aesToBeDeleted = m_aesDtc->bCContaminations().keys();
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
|
||||
AreaEffectSimulation::BCContaminationModifyRequest req;
|
||||
req.weatherSource(AreaEffectSimulation::AesWeatherSource::AwsNoUpdate);
|
||||
uint64_t tmp;
|
||||
int x = 0, y = 0;
|
||||
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
getBasicData(data, pos, req);
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.effectDuration(tmp * 60 * 1000);
|
||||
LtsUtils::get3Byte(data, pos, y); // north
|
||||
LtsUtils::get3Byte(data, pos, x); // east
|
||||
req.impactPosition(relativeToGeoPosition(x, y));
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.impactRadius(tmp);
|
||||
LtsUtils::get1Byte(data, pos, tmp); // expanding factor -> is deduced from ChargeType
|
||||
LtsUtils::get2Byte(data, pos, tmp); // wind direction
|
||||
req.propagationDirection(dds::core::optional<double>(compassToMathAngleForSfm(tmp))); // what an angle
|
||||
LtsUtils::get1Byte(data, pos, tmp); // wind speed
|
||||
req.windSpeed(dds::core::optional<double>(tmp));
|
||||
LtsUtils::get4Byte(data, pos, tmp); // timestamp since when weather is valid
|
||||
req.weatherTimestamp(dds::core::optional<rti::core::int64>(tmp * 1000)); // secs to msecs
|
||||
LtsUtils::get3Byte(data, pos, y); // north when weather changed
|
||||
LtsUtils::get3Byte(data, pos, x); // east when weather changed
|
||||
req.weatherPosition(dds::core::optional<Core::GeoPosition2D>(relativeToGeoPosition(x, y)));
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Chemical, tmp));
|
||||
if (req.chargeTypeId() == 0)
|
||||
req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Biological, tmp));
|
||||
LtsUtils::get1Byte(data, pos, tmp); // votality type -> is deduced from ChargeType
|
||||
|
||||
DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "BC contamination: " << req;
|
||||
|
||||
aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted
|
||||
req.id(req.id() * -1); // inverted id to mark as for oc
|
||||
|
||||
m_aesFtc->bCContaminationModifyRequest(req, this);
|
||||
}
|
||||
|
||||
for (int id : qAsConst(aesToBeDeleted))
|
||||
{
|
||||
if (m_aesDtc->bCContamination(id).isCentral())
|
||||
continue;
|
||||
if (m_aesDtc->bCContamination(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Removing BC contamination id: " << id;
|
||||
m_aesFtc->bCContaminationRemoveRequest(Core::RemoveElementRequest(id), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::explosionAreas(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got new explosion cluster data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
QList<int> aesToBeDeleted = m_aesDtc->explosionClusters().keys();
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
|
||||
AreaEffectSimulation::ExplosionClusterOcModifyRequest req;
|
||||
uint64_t tmp;
|
||||
int x = 0, y = 0;
|
||||
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
// basic data
|
||||
LtsUtils::get2Byte(data, pos, tmp); // id
|
||||
req.id(tmp);
|
||||
|
||||
req.name(data.mid(pos, 8).toStdString()); // name
|
||||
QString name = QString::fromStdString(req.name());
|
||||
// Linux doesn't like it when there are non plausable charaters
|
||||
name = name.replace(QRegExp(QStringLiteral("[^ -~]")), QLatin1String(""));
|
||||
name = name.trimmed();
|
||||
if (!name.isEmpty())
|
||||
req.name(name.toStdString());
|
||||
|
||||
pos += 8;
|
||||
LtsUtils::get1Byte(data, pos, tmp); // party id
|
||||
req.partyId(tmp);
|
||||
|
||||
LtsUtils::get4Byte(data, pos, tmp); // effect start
|
||||
req.effectStart(tmp * 1000);
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp); // effect duration
|
||||
req.effectDuration(tmp * 1000);
|
||||
|
||||
// area
|
||||
AreaEffectSimulation::Area area;
|
||||
LtsUtils::get3Byte(data, pos, y); // north
|
||||
LtsUtils::get3Byte(data, pos, x); // east
|
||||
area.cornerBottomLeft(relativeToGeoPosition(x, y));
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
area.width(tmp * 5);
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
area.height(tmp * 5);
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
area.rotation(mathToCompassAngle(tmp + 9000)); // +9000, as other edge is used as reference to rotate in the sfm
|
||||
req.area(area);
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp); // mhp width (1 byte) and length (1 byte) --> just read and skip, since it is not needed here
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp); // mhp rotation
|
||||
req.mhpRotation(mathToCompassAngle(tmp + 9000)); // +9000, as other edge is used as reference to rotate in the sfm
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp); // impact count
|
||||
req.impactCount(tmp);
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp); // impact edge length --> just read and skip, since it is not needed here
|
||||
|
||||
LtsUtils::get1Byte(data, pos, tmp); // charge type id
|
||||
req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Artillery, tmp));
|
||||
if (req.chargeStruct().chargeTypeId() == 0)
|
||||
req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Mortar, tmp));
|
||||
if (req.chargeStruct().chargeTypeId() == 0)
|
||||
req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::AirSupport, tmp));
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp); // hazard distance (1 byte) and hazard type (1 byte) --> just read and skip, since it is not needed here
|
||||
|
||||
LtsUtils::get1Byte(data, pos, tmp); // mhp count
|
||||
req.mhpCount(tmp);
|
||||
|
||||
// mhps --> just read and skip, since it is not needed here
|
||||
for (int32_t mhpCounter = 0; mhpCounter < req.mhpCount(); ++mhpCounter)
|
||||
{
|
||||
LtsUtils::get3Byte(data, pos, tmp);
|
||||
LtsUtils::get3Byte(data, pos, tmp);
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "New ExplosionClusterOcModifyRequest: "
|
||||
<< "\nid:" << req.id() << "\nname:" << req.name() << "\neffectStart:" << req.effectStart() << "\neffectDuration:" << req.effectDuration()
|
||||
<< "\narea:" << req.area() << "\nmhpCount:" << req.mhpCount() << "\nmhpRotation:" << req.mhpRotation() << "\nimpactCount:" << req.impactCount()
|
||||
<< "\nchargeTypeId:" << req.chargeStruct().chargeTypeId() << "\npartyId:" << req.partyId();
|
||||
|
||||
DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "explosion cluster: " << req;
|
||||
aesToBeDeleted.removeOne(req.id()); // item was created/modified -> does not need to be deleted
|
||||
req.id(req.id() * -1); // inverted id to mark as for oc
|
||||
|
||||
m_aesFtc->explosionClusterOcModifyRequest(req, this);
|
||||
}
|
||||
|
||||
for (int id : qAsConst(aesToBeDeleted))
|
||||
{
|
||||
const AreaEffectSimulation::ExplosionCluster &exp = m_aesDtc->explosionCluster(id);
|
||||
|
||||
if (exp.isCentral())
|
||||
continue;
|
||||
|
||||
if ((exp.effectStateId().underlying() >= AreaEffectSimulation::AesEffectState::EsPrepared)
|
||||
|| ((exp.effectStateId().underlying() >= AreaEffectSimulation::AesEffectState::EsActivated)
|
||||
&& (((exp.effectStart().is_set() && ((exp.effectStart().get() + exp.effectDuration())) < (m_dds->currentPartitionTime() - 5000)))
|
||||
|| // is not recently expired
|
||||
(exp.effectStateId().underlying() == AreaEffectSimulation::AesEffectState::EsPaused)))) // or is paused
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Removing explosion cluster with id: " << id;
|
||||
m_aesFtc->explosionClusterRemoveRequest(Core::RemoveElementRequest(id), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::explosionSingles(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got new explosion single data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
QList<int> aesToBeDeleted = m_aesDtc->explosionSingles().keys();
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
|
||||
AreaEffectSimulation::ExplosionSingleModifyRequest req;
|
||||
uint64_t tmp;
|
||||
int x = 0, y = 0;
|
||||
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
getBasicData(data, pos, req);
|
||||
|
||||
LtsUtils::get3Byte(data, pos, y); // north
|
||||
LtsUtils::get3Byte(data, pos, x); // east
|
||||
req.impactPosition(relativeToGeoPosition(x, y));
|
||||
if (m_use2BytesForSingleFireDiameter)
|
||||
LtsUtils::get2Byte(data, pos, tmp); // impact radius -> is deduced from ChargeType
|
||||
else
|
||||
LtsUtils::get1Byte(data, pos, tmp); // impact radius -> is deduced from ChargeType
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Artillery, tmp));
|
||||
if (req.chargeTypeId() == 0)
|
||||
req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Mortar, tmp));
|
||||
DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "explosion single: " << req;
|
||||
|
||||
aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted
|
||||
req.id(req.id() * -1); // inverted id to mark as for oc
|
||||
m_aesFtc->explosionSingleModifyRequest(req, this);
|
||||
}
|
||||
|
||||
for (int id : qAsConst(aesToBeDeleted))
|
||||
{
|
||||
const AreaEffectSimulation::ExplosionSingle &exp = m_aesDtc->explosionSingle(id);
|
||||
if (exp.isCentral())
|
||||
continue;
|
||||
if ((exp.effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved)
|
||||
&& (exp.effectStart().is_set() && (exp.effectStart().get() > (m_dds->currentPartitionTime() + 5000)))) // is not about to expire
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Removing explosion single id: " << id;
|
||||
m_aesFtc->explosionSingleRemoveRequest(Core::RemoveElementRequest(id), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::functionalAreas(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got functional area data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
QList<int> aesToBeDeleted = m_aesDtc->functionalAreas().keys();
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
|
||||
AreaEffectSimulation::FunctionalAreaModifyRequest req;
|
||||
uint64_t tmp;
|
||||
int x = 0, y = 0;
|
||||
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
getBasicData(data, pos, req, true);
|
||||
if (!aesToBeDeleted.contains(req.id())) // is a create?
|
||||
req.effectStart(dds::core::optional<rti::core::int64>(m_dds->currentPartitionTime() + 100)); // functional areas always start on create
|
||||
|
||||
// area
|
||||
AreaEffectSimulation::Area area;
|
||||
LtsUtils::get3Byte(data, pos, y); // north
|
||||
LtsUtils::get3Byte(data, pos, x); // east
|
||||
area.cornerBottomLeft(relativeToGeoPosition(x, y));
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
area.width(tmp * 5);
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
area.height(tmp * 5);
|
||||
area.rotation(0);
|
||||
req.area(area);
|
||||
|
||||
// modes and times
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.deconMode(static_cast<AreaEffectSimulation::AesOperatingMode::inner_enum>(tmp));
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.deconTime(tmp * 10 * 60 * 1000); // to ms
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.maintMode(static_cast<AreaEffectSimulation::AesOperatingMode::inner_enum>(tmp));
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.maintTime(tmp * 10 * 60 * 1000); // to ms
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.medicMode(static_cast<AreaEffectSimulation::AesOperatingMode::inner_enum>(tmp));
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.medicTime(tmp * 10 * 60 * 1000); // to ms
|
||||
req.chargeTypeId(411); // Dummy
|
||||
|
||||
DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "functional area: " << req;
|
||||
|
||||
aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted
|
||||
req.id(req.id() * -1); // inverted id to mark as for oc
|
||||
|
||||
m_aesFtc->functionalAreaModifyRequest(req, this);
|
||||
}
|
||||
|
||||
for (int id : qAsConst(aesToBeDeleted))
|
||||
{
|
||||
if (m_aesDtc->functionalArea(id).isCentral())
|
||||
continue;
|
||||
if (m_aesDtc->functionalArea(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Removing functional area id: " << id;
|
||||
m_aesFtc->functionalAreaRemoveRequest(Core::RemoveElementRequest(id), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::nContaminations(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got new N contamination data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
QList<int> aesToBeDeleted = m_aesDtc->nContaminations().keys();
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
|
||||
AreaEffectSimulation::NContaminationModifyRequest req;
|
||||
uint64_t tmp;
|
||||
int x = 0, y = 0;
|
||||
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
getBasicData(data, pos, req);
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.effectDuration(tmp * 6 * 60 * 1000);
|
||||
LtsUtils::get3Byte(data, pos, y); // north
|
||||
LtsUtils::get3Byte(data, pos, x); // east
|
||||
req.impactPosition(relativeToGeoPosition(x, y));
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.impactRadius(tmp);
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.borderLengthInner(tmp);
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.borderLengthOuter(tmp);
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.spanAngle(tmp / 100.0);
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.rotationAngle(mathToCompassAngle(tmp));
|
||||
|
||||
req.chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::Nuclear, 1));
|
||||
|
||||
DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "N contamination: " << req;
|
||||
|
||||
aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted
|
||||
req.id(req.id() * -1); // inverted id to mark as for oc
|
||||
|
||||
m_aesFtc->nContaminationModifyRequest(req, this);
|
||||
}
|
||||
|
||||
for (int id : qAsConst(aesToBeDeleted))
|
||||
{
|
||||
if (m_aesDtc->nContamination(id).isCentral())
|
||||
continue;
|
||||
if (m_aesDtc->nContamination(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Removing N contamination id: " << id;
|
||||
m_aesFtc->nContaminationRemoveRequest(Core::RemoveElementRequest(id), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::mineAreas(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got new mine area data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
QList<int> aesToBeDeleted = m_aesDtc->mineAreas().keys();
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
|
||||
AreaEffectSimulation::MineArea req;
|
||||
uint64_t tmp;
|
||||
int x = 0, y = 0;
|
||||
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
getBasicData(data, pos, req);
|
||||
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.effectDuration(tmp * 60 * 60 * 1000); // convert to msecs
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.width(tmp);
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
req.density(tmp / 100.0);
|
||||
|
||||
LtsUtils::get1Byte(data, pos, tmp);
|
||||
if (217 > tmp && 210 < tmp) // Legacy Ids
|
||||
req.chargeStruct().chargeTypeId(chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass_def::MineArea, tmp));
|
||||
else // OSAG = ChargeTypeIs
|
||||
req.chargeStruct().chargeTypeId(tmp);
|
||||
|
||||
LtsUtils::get2Byte(data, pos, tmp); // total mine count
|
||||
|
||||
// segments
|
||||
int segmentCnt = 0;
|
||||
LtsUtils::get1Byte(data, pos, segmentCnt);
|
||||
req.segments().resize(segmentCnt);
|
||||
|
||||
for (int j = 0; j < segmentCnt; ++j)
|
||||
{
|
||||
AreaEffectSimulation::MineSegment &segment = req.segments().at(j);
|
||||
LtsUtils::get3Byte(data, pos, y);
|
||||
LtsUtils::get3Byte(data, pos, x);
|
||||
segment.positionStart(relativeToGeoPosition(x, y));
|
||||
LtsUtils::get3Byte(data, pos, y);
|
||||
LtsUtils::get3Byte(data, pos, x);
|
||||
segment.positionEnd(relativeToGeoPosition(x, y));
|
||||
}
|
||||
|
||||
req.isCenterLine(true);
|
||||
|
||||
DEBUG_LOG(m_logger) << (aesToBeDeleted.contains(req.id()) ? "Modifying " : "Creating ") << "mine area: " << req;
|
||||
|
||||
aesToBeDeleted.removeAll(req.id()); // item was created/modified -> does not need to be deleted
|
||||
req.id(req.id() * -1); // inverted id to mark as for oc
|
||||
|
||||
req.setMineSorted(false);
|
||||
m_aesFtc->mineAreaModifyFromCtaDataRequest(req, this);
|
||||
}
|
||||
|
||||
for (int id : qAsConst(aesToBeDeleted))
|
||||
{
|
||||
if (m_aesDtc->mineArea(id).isCentral())
|
||||
continue;
|
||||
if (m_aesDtc->mineArea(id).effectStateId().underlying() > AreaEffectSimulation::AesEffectState::EsRemoved)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Removing mine area id: " << id;
|
||||
m_aesFtc->mineAreaRemoveRequest(Core::RemoveElementRequest(id), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::explosionAreasPause(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got new explosion area pause data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
|
||||
uint64_t id;
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
LtsUtils::get2Byte(data, pos, id);
|
||||
DEBUG_LOG(m_logger) << "Pausing ID: " << id;
|
||||
m_aesFtc->explosionAreaPauseResumeRequest(AreaEffectSimulation::ExplosionAreaPauseResumeRequest(id,
|
||||
false,
|
||||
dds::core::optional<rti::core::int64>(),
|
||||
dds::core::optional<int32_t>(),
|
||||
dds::core::optional<int32_t>()),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
void HandlerAesData::explosionAreasResume(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got new explosion area resume data";
|
||||
DEBUG_LOG(m_logger) << "Binary data: " << data.toHex(' ');
|
||||
|
||||
size_t pos = 0;
|
||||
int cnt;
|
||||
LtsUtils::get1Byte(data, pos, cnt);
|
||||
uint64_t tmp;
|
||||
AreaEffectSimulation::ExplosionAreaPauseResumeRequest req;
|
||||
req.resumeExecution(true);
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
{
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.id(tmp);
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.resumeSeed(dds::core::optional<int32_t>(tmp));
|
||||
LtsUtils::get2Byte(data, pos, tmp); // number of impact -> not used, calculated internally
|
||||
LtsUtils::get4Byte(data, pos, tmp);
|
||||
req.timeToResume(dds::core::optional<rti::core::int64>(tmp * 1000));
|
||||
LtsUtils::get2Byte(data, pos, tmp);
|
||||
req.remainingDuration(dds::core::optional<int32_t>(tmp));
|
||||
DEBUG_LOG(m_logger) << "Resuming: " << req;
|
||||
m_aesFtc->explosionAreaPauseResumeRequest(req, this);
|
||||
}
|
||||
}
|
||||
|
||||
int HandlerAesData::chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass gearClass, int chargeTasCodeIndirect)
|
||||
{
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
if (!m_pdmDtc->waitForHistoricalDataChargeType(20000)) // 20s
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Can not read charge types from PDM";
|
||||
QCoreApplication::exit(-1);
|
||||
return -1;
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return m_pdmDtc->chargeTypeIdFromLegacyId(gearClass, chargeTasCodeIndirect);
|
||||
}
|
||||
|
||||
Core::GeoPosition2D HandlerAesData::relativeToGeoPosition(int x, int y) const
|
||||
{
|
||||
const QPointF point(m_utmCoord.geographic(x, y));
|
||||
return Core::GeoPosition2D(point.x(), point.y());
|
||||
}
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Handler for AES data received from SFM
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-04-11
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
|
||||
#include <AreaEffectSimulation/aes_dtc.h>
|
||||
#include <AreaEffectSimulation/aes_ftc.h>
|
||||
#include <ProjectDataManagement/pdm_dtc.h>
|
||||
|
||||
#include "oc_telegrams.h"
|
||||
#include "dds_handler.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class HandlerAesData
|
||||
: protected AreaEffectSimulation::Interface::Ftc::BCContaminationModifyReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaModifyReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::ExplosionSingleModifyReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::FunctionalAreaModifyReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::NContaminationModifyReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::MineAreaModifyFromCtaDataReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::BCContaminationRemoveReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaRemoveReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::ExplosionSingleRemoveReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::FunctionalAreaRemoveReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::NContaminationRemoveReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::MineAreaRemoveReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaPauseResumeReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterOcModifyReplyHandler
|
||||
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterRemoveReplyHandler
|
||||
{
|
||||
public:
|
||||
explicit HandlerAesData(const std::shared_ptr<DdsHandler> &dds,
|
||||
const std::shared_ptr<AreaEffectSimulation::Interface::Dtc> &aesDtc,
|
||||
const std::shared_ptr<AreaEffectSimulation::Interface::Ftc> &aesFtc,
|
||||
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc);
|
||||
|
||||
bool init();
|
||||
void setUse2BytesForSingleFireDiameter(bool bUse) { m_use2BytesForSingleFireDiameter = bUse; }
|
||||
|
||||
void process(const QByteArray &data, int multiplexer);
|
||||
|
||||
void specificsExercise(const OcTelegrams::SpecificsExercise &data);
|
||||
|
||||
void onMineAreaModifyFromCtaDataReply(const Core::FunctionReplyObject &reply);
|
||||
void onNContaminationModifyReply(const Core::FunctionReplyObject &reply);
|
||||
void onFunctionalAreaModifyReply(const Core::FunctionReplyObject &reply);
|
||||
void onExplosionSingleModifyReply(const Core::FunctionReplyObject &reply);
|
||||
void onExplosionAreaModifyReply(const Core::FunctionReplyObject &reply);
|
||||
void onBCContaminationModifyReply(const Core::FunctionReplyObject &reply);
|
||||
void onExplosionClusterOcModifyReply(const Core::FunctionReplyObject &reply);
|
||||
|
||||
void onMineAreaRemoveReply(const Core::FunctionReplyObject &reply);
|
||||
void onNContaminationRemoveReply(const Core::FunctionReplyObject &reply);
|
||||
void onFunctionalAreaRemoveReply(const Core::FunctionReplyObject &reply);
|
||||
void onExplosionSingleRemoveReply(const Core::FunctionReplyObject &reply);
|
||||
void onExplosionAreaRemoveReply(const Core::FunctionReplyObject &reply);
|
||||
void onBCContaminationRemoveReply(const Core::FunctionReplyObject &reply);
|
||||
void onExplosionClusterRemoveReply(const Core::FunctionReplyObject &reply);
|
||||
|
||||
void onExplosionAreaPauseResumeReply(const Core::FunctionReplyObject &reply);
|
||||
|
||||
private:
|
||||
void bcContaminations(const QByteArray &data);
|
||||
void explosionAreas(const QByteArray &data);
|
||||
void explosionSingles(const QByteArray &data);
|
||||
void functionalAreas(const QByteArray &data);
|
||||
void nContaminations(const QByteArray &data);
|
||||
void mineAreas(const QByteArray &data);
|
||||
void explosionAreasPause(const QByteArray &data);
|
||||
void explosionAreasResume(const QByteArray &data);
|
||||
|
||||
int chargeTypeIdFromChargeTasCode(ProjectDataManagement::GearClass gearClass, int chargeTasCodeIndirect);
|
||||
|
||||
Core::GeoPosition2D relativeToGeoPosition(int x, int y) const;
|
||||
|
||||
enum
|
||||
{
|
||||
MineArea = 1,
|
||||
ExplosionArea = 2,
|
||||
ExplosionSingle = 3,
|
||||
NContamination = 4,
|
||||
BcContamination = 5,
|
||||
FunctionalArea = 6,
|
||||
ExplosionAreaStop = 7,
|
||||
ExplosionAreaContinue = 8
|
||||
};
|
||||
|
||||
std::shared_ptr<DdsHandler> m_dds;
|
||||
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
|
||||
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_aesFtc;
|
||||
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
|
||||
|
||||
LtsCoordTrans::UtmCoordinate m_utmCoord; // for position translation to relative
|
||||
|
||||
bool m_use2BytesForSingleFireDiameter = true;
|
||||
|
||||
LOGGER_T &m_logger;
|
||||
};
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
log4j.reset=true
|
||||
log4j.rootLogger=WARN, file
|
||||
|
||||
# Service: TrainingAreaBase
|
||||
###########################################################
|
||||
|
||||
#log4j.logger.TABOC=DEBUG
|
||||
#log4j.logger.TABOC.main=DEBUG
|
||||
#log4j.logger.TABOC.DdsHandler=DEBUG
|
||||
#log4j.logger.TABOC.MainHandler=DEBUG
|
||||
#log4j.logger.TABOC.ConnectionHandler=DEBUG
|
||||
#log4j.logger.TABOC.CanagHandler=DEBUG
|
||||
#log4j.logger.TABOC.TxDdlHandler=DEBUG
|
||||
#log4j.logger.TABOC.RxDdlHandler=DEBUG
|
||||
#log4j.logger.TABOC.HandlerAesData=DEBUG
|
||||
#log4j.logger.TABOC.RecvFromDdsFts=DEBUG
|
||||
#log4j.logger.TABOC.EtsRxDds=DEBUG
|
||||
|
||||
# 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=DEBUG
|
||||
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:
|
||||
Executable
+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 TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "TABOC.";
|
||||
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 TrainingAreaBase
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Logging for TrainingAreaBase
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include "taboc_export.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
using LOGGER_T = NGL::INGLLogger&;
|
||||
|
||||
TABOC_COMMON_EXPORT LOGGER_T getLogger(const char *name);
|
||||
inline LOGGER_T getLogger(const QString &loggerName)
|
||||
{
|
||||
return getLogger(loggerName.toUtf8().constData());
|
||||
}
|
||||
} // namespace TrainingAreaBase
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* main for OC-connections to SFM
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <LtsCommandLineParser/LtsCommandLineParser.h>
|
||||
#include <QNetworkProxy>
|
||||
#include <memory>
|
||||
#include "main_handler.h"
|
||||
#include "settings.h"
|
||||
#include "logger.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NGLCoreApplication app(argc, argv);
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
|
||||
int iRet = -1;
|
||||
LtsCommandLineParser::LtsCommandLineParser options;
|
||||
if (!options.init())
|
||||
return iRet;
|
||||
app.createLogger("TABOC", options.exerciseId());
|
||||
|
||||
TrainingAreaBaseOC::Settings::setIniFile(options.iniFile());
|
||||
|
||||
{
|
||||
std::unique_ptr<TrainingAreaBaseOC::MainHandler> mainHandler(new TrainingAreaBaseOC::MainHandler);
|
||||
if (mainHandler->init(options.domainId(), options.exerciseId()))
|
||||
{
|
||||
STARTUP_LOG(app.baseLogger());
|
||||
iRet = app.exec();
|
||||
SHUTDOWN_LOG(app.baseLogger());
|
||||
}
|
||||
}
|
||||
|
||||
return iRet;
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* MainHandler for OC-connection to the SFM
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
|
||||
#include "main_handler.h"
|
||||
#include "settings.h"
|
||||
#include "logger.h"
|
||||
#include <DDS/Core/pacemaker.h>
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
MainHandler::MainHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_aesDtc(nullptr)
|
||||
, m_aesFtc(nullptr)
|
||||
, m_pdmDtc(nullptr)
|
||||
, m_canagHandler(nullptr)
|
||||
, m_connectionHandler(nullptr)
|
||||
, m_ddsHandler(nullptr)
|
||||
, m_rxHandler(nullptr)
|
||||
, m_ftsHandler(nullptr)
|
||||
, m_websocketHandler(nullptr)
|
||||
, m_logger(getLogger("MainHandler"))
|
||||
{}
|
||||
|
||||
MainHandler::~MainHandler()
|
||||
{
|
||||
m_rxHandler.reset();
|
||||
m_canagHandler.reset();
|
||||
m_connectionHandler.reset();
|
||||
m_ftsHandler.reset();
|
||||
m_aesDtc.reset();
|
||||
m_aesFtc.reset();
|
||||
m_pdmDtc.reset();
|
||||
m_ddsHandler.reset();
|
||||
}
|
||||
|
||||
bool MainHandler::init(int domainId, int exerciseId)
|
||||
{
|
||||
// initialize DDS
|
||||
m_ddsHandler = std::make_shared<DdsHandler>(domainId, exerciseId);
|
||||
if (!m_ddsHandler->init())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not initialise DdsHandler.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// create AES Dtc
|
||||
m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsHandler->ddsConnector());
|
||||
DEBUG_LOG(m_logger) << "Created AES Dtc";
|
||||
|
||||
// create AES Ftc
|
||||
m_aesFtc = AreaEffectSimulation::Interface::Ftc::create(m_ddsHandler->ddsConnector());
|
||||
DEBUG_LOG(m_logger) << "Created AES Ftc";
|
||||
|
||||
// create PDM Dtc
|
||||
m_pdmDtc = ProjectDataManagement::Interface::Dtc::create(m_ddsHandler->ddsConnector());
|
||||
DEBUG_LOG(m_logger) << "Created PDM Dtc";
|
||||
|
||||
// ConnectionHandler
|
||||
m_connectionHandler = std::make_shared<ConnectionHandler>();
|
||||
|
||||
if (!m_connectionHandler->init())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not initialise ConnectionHandler.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// to DDS
|
||||
m_rxHandler = std::make_shared<SendToDds>(this);
|
||||
|
||||
// CanagHandler / to TrainingArea
|
||||
m_canagHandler = std::make_shared<CanagHandler>(this);
|
||||
|
||||
if (!m_canagHandler->init())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not initialise CanagHandler.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// from DDS
|
||||
m_ftsHandler = std::make_shared<RecvFromDdsFts>(this);
|
||||
#ifndef GUEZ_MODE
|
||||
m_websocketHandler = std::make_shared<WebsocketHandler>(this, exerciseId, m_ddsHandler->dtsTaboc(), m_ddsHandler->dtcTaboc());
|
||||
#endif
|
||||
|
||||
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::Running);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* MainHandler for OC-connection to the SFM
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include <AreaEffectSimulation/aes_dtc.h>
|
||||
#include <AreaEffectSimulation/aes_ftc.h>
|
||||
#include <ProjectDataManagement/pdm_dtc.h>
|
||||
|
||||
#include "canag_handler.h"
|
||||
#include "connection_handler.h"
|
||||
#include "dds_handler.h"
|
||||
#include "send_to_dds.h"
|
||||
#include "recv_from_dds_fts.h"
|
||||
#include "recv_from_tab.h"
|
||||
#include "websocket_handler.h"
|
||||
#include "logger.h"
|
||||
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class MainHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainHandler(QObject *parent = nullptr);
|
||||
~MainHandler();
|
||||
|
||||
bool init(int domainId, int exerciseId);
|
||||
|
||||
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> aesDtc() { return m_aesDtc; }
|
||||
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> aesFtc() { return m_aesFtc; }
|
||||
std::shared_ptr<ProjectDataManagement::Interface::Dtc> pdmDtc() { return m_pdmDtc; }
|
||||
|
||||
std::shared_ptr<CanagHandler> canagHandler() const { return m_canagHandler; }
|
||||
std::shared_ptr<ConnectionHandler> connectionHandler() const { return m_connectionHandler; }
|
||||
std::shared_ptr<DdsHandler> dds() const { return m_ddsHandler; }
|
||||
std::shared_ptr<RecvFromTab> rxHandler() const { return m_rxHandler; }
|
||||
std::shared_ptr<RecvFromDdsFts> ftsHandler() const { return m_ftsHandler; }
|
||||
std::shared_ptr<SendToTab> txHandler() const { return m_canagHandler; }
|
||||
|
||||
int exerciseId() const { return m_ddsHandler->ddsConnector()->getPartitionId(); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
|
||||
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_aesFtc;
|
||||
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
|
||||
|
||||
std::shared_ptr<CanagHandler> m_canagHandler;
|
||||
std::shared_ptr<ConnectionHandler> m_connectionHandler;
|
||||
std::shared_ptr<DdsHandler> m_ddsHandler;
|
||||
std::shared_ptr<RecvFromTab> m_rxHandler;
|
||||
std::shared_ptr<RecvFromDdsFts> m_ftsHandler;
|
||||
std::shared_ptr<WebsocketHandler> m_websocketHandler;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Structures used for messages to/from the OC-Laptop
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-02-02
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
namespace OcTelegrams
|
||||
{
|
||||
enum class TextType
|
||||
{
|
||||
test = 0,
|
||||
UnderFire = 1,
|
||||
Injury = 2,
|
||||
Manipulation = 3,
|
||||
Kill = 4,
|
||||
State = 5,
|
||||
Error = 6,
|
||||
Activation = 7,
|
||||
Messages = 8,
|
||||
AcousticOutputOnly = 9,
|
||||
ClearDisplay = 255,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_DISPLAY */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct Display
|
||||
{
|
||||
QByteArray text;
|
||||
int multiplexer = 0;
|
||||
TextType textType = TextType::test;
|
||||
int indexVbg = 0;
|
||||
int indexVba = 0;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_TIME */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct TimeSync
|
||||
{
|
||||
qint64 gpsTimeMsec = 0;
|
||||
unsigned char timezone = '\0';
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_ENTER_LEAVE_AES */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct EnterLeaveAes
|
||||
{
|
||||
unsigned int aesId = 0;
|
||||
unsigned int chargeTasCode = 0;
|
||||
bool aesEntered = false;
|
||||
unsigned char nbcArea = '\0';
|
||||
unsigned char cAreaType = '\0';
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_TECHNICAL_STATE_SFM */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct TechnicalStateSfm
|
||||
{
|
||||
qint64 gpsTimeMsec = 0;
|
||||
bool sfmConnected = false;
|
||||
bool radioAvailable = false;
|
||||
bool licensed = false;
|
||||
bool batteryTest = false;
|
||||
bool manipulationEquipment = false;
|
||||
bool manipulationSfm = false;
|
||||
unsigned char errorCode;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_DEMAND_SELFTEST_SFM */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct DemandSelftestSfm
|
||||
{
|
||||
char dummy = '\0';
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_OWN_POSITION */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct OwnPosition
|
||||
{
|
||||
qint64 gpsTimeMsec = 0;
|
||||
int easting = 0;
|
||||
int northing = 0;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_DEMAND_CYCLIC_POSISTIONS */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct DemandCyclicPositions
|
||||
{
|
||||
unsigned char cycleTime = '\1';
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_SPECIFICS_EXERCISE */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct SpecificsExercise
|
||||
{
|
||||
QByteArray square;
|
||||
unsigned int exerciseId = 0;
|
||||
unsigned int zone = 0;
|
||||
unsigned int easting = 0;
|
||||
unsigned int northing = 0;
|
||||
unsigned char band;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_SPECIFICS_SFM */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct SpecificsEquipment
|
||||
{
|
||||
unsigned int equipmentNumber = 0;
|
||||
TrainingAreaBase::TetraIcd::Language language;
|
||||
unsigned char playerType = '\0';
|
||||
unsigned char sfm = '\0';
|
||||
unsigned char panCoordinator = '\0';
|
||||
unsigned char viss = '\0';
|
||||
unsigned char geopairing = '\0';
|
||||
unsigned char ecu = '\0';
|
||||
unsigned char hsu = '\0';
|
||||
unsigned char halt = '\0';
|
||||
bool pyrotechnics = false;
|
||||
bool voiceOutput = false;
|
||||
bool exerciseBreak = false;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* OC_TECHNICAL_STATE_OC */
|
||||
/*----------------------------------------------------------------*/
|
||||
struct TechnicalStateOc
|
||||
{
|
||||
unsigned char errorCode = '\0';
|
||||
};
|
||||
|
||||
} // namespace OcTelegrams
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Handle incoming Function Topic Requests (and send them to SFM)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
|
||||
#include "recv_from_dds_fts.h"
|
||||
#include "dds_handler.h"
|
||||
#include "main_handler.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
RecvFromDdsFts::RecvFromDdsFts(MainHandler *mainHandler)
|
||||
: Interface::Fts::DemandCyclicPositionsRequestHandler(mainHandler->dds()->ftsTaboc())
|
||||
, Interface::Fts::DemandSfmSelfTestRequestHandler(mainHandler->dds()->ftsTaboc())
|
||||
, Interface::Fts::SendTechnicalStateOcRequestHandler(mainHandler->dds()->ftsTaboc())
|
||||
, TrainingAreaBase::Interface::Fts::SendOcDataRequestHandler(mainHandler->dds()->ftsTab())
|
||||
, TrainingAreaBase::Interface::Fts::SendFireSmokeMarkerDataRequestHandler(mainHandler->dds()->ftsTab())
|
||||
, m_ftsTaboc(mainHandler->dds()->ftsTaboc())
|
||||
, m_ftsTab(mainHandler->dds()->ftsTab())
|
||||
, m_txCallback(mainHandler->txHandler())
|
||||
, m_logger(getLogger("RecvFromDdsFts"))
|
||||
{}
|
||||
|
||||
void RecvFromDdsFts::onDemandCyclicPositionsRequest(const rti::core::SampleIdentity &cookie, const DemandCyclicPositionsRequest &request)
|
||||
{
|
||||
OcTelegrams::DemandCyclicPositions data;
|
||||
data.cycleTime = request.cycleTime();
|
||||
|
||||
m_ftsTaboc->demandCyclicPositionsReply(cookie, fillFro(m_txCallback->txData(data)));
|
||||
}
|
||||
|
||||
void RecvFromDdsFts::onDemandSfmSelfTestRequest(const rti::core::SampleIdentity &cookie, const DemandSfmSelfTestRequest &)
|
||||
{
|
||||
OcTelegrams::DemandSelftestSfm data;
|
||||
m_ftsTaboc->demandSfmSelfTestReply(cookie, fillFro(m_txCallback->txData(data)));
|
||||
}
|
||||
|
||||
void RecvFromDdsFts::onSendTechnicalStateOcRequest(const rti::core::SampleIdentity &cookie, const SendTechnicalStateOcRequest &request)
|
||||
{
|
||||
OcTelegrams::TechnicalStateOc data;
|
||||
data.errorCode = request.errorCode();
|
||||
|
||||
m_ftsTaboc->sendTechnicalStateOcReply(cookie, fillFro(m_txCallback->txData(data)));
|
||||
}
|
||||
|
||||
void RecvFromDdsFts::onSendOcDataRequest(const rti::core::SampleIdentity &cookie, const TrainingAreaBase::SendOcDataRequest &request)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::OpaquePayloadDataOut data;
|
||||
data.payloadType = TrainingAreaBase::TetraIcd::OpaquePayloadDataBase::Type::OC;
|
||||
data.payload = request.payload();
|
||||
|
||||
m_ftsTab->sendOcDataReply(cookie, fillFro(m_txCallback->txData(data)));
|
||||
}
|
||||
|
||||
void RecvFromDdsFts::onSendFireSmokeMarkerDataRequest(const rti::core::SampleIdentity &cookie, const TrainingAreaBase::SendFireSmokeMarkerDataRequest &request)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::OpaquePayloadDataOut data;
|
||||
data.payloadType = TrainingAreaBase::TetraIcd::OpaquePayloadDataBase::Type::FireSmokeMarker;
|
||||
data.payload = request.payload();
|
||||
|
||||
m_ftsTab->sendFireSmokeMarkerDataReply(cookie, fillFro(m_txCallback->txData(data)));
|
||||
}
|
||||
|
||||
Core::FunctionReplyObject RecvFromDdsFts::fillFro(bool bSuccess)
|
||||
{
|
||||
if (bSuccess)
|
||||
return Core::FunctionReplyObject();
|
||||
return Core::FunctionReplyObject(tr_lts("dds", "Error sending telegram to sfm."));
|
||||
}
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Handle incoming Function Topic Requests (and send them to SFM)
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <TrainingAreaBaseOC/taboc_fts.h>
|
||||
#include <TrainingAreaBase/tab_fts.h>
|
||||
#include "logger.h"
|
||||
#include "send_to_tab.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class MainHandler;
|
||||
|
||||
class RecvFromDdsFts final
|
||||
: protected Interface::Fts::DemandCyclicPositionsRequestHandler
|
||||
, protected Interface::Fts::DemandSfmSelfTestRequestHandler
|
||||
, protected Interface::Fts::SendTechnicalStateOcRequestHandler
|
||||
, protected TrainingAreaBase::Interface::Fts::SendOcDataRequestHandler
|
||||
, protected TrainingAreaBase::Interface::Fts::SendFireSmokeMarkerDataRequestHandler
|
||||
{
|
||||
public:
|
||||
explicit RecvFromDdsFts(MainHandler *mainHandler);
|
||||
|
||||
void onDemandCyclicPositionsRequest(const rti::core::SampleIdentity &cookie, const DemandCyclicPositionsRequest &request) override;
|
||||
void onDemandSfmSelfTestRequest(const rti::core::SampleIdentity &cookie, const DemandSfmSelfTestRequest &request) override;
|
||||
void onSendTechnicalStateOcRequest(const rti::core::SampleIdentity &cookie, const SendTechnicalStateOcRequest &request) override;
|
||||
void onSendOcDataRequest(const rti::core::SampleIdentity &cookie, const TrainingAreaBase::SendOcDataRequest &request) override;
|
||||
void onSendFireSmokeMarkerDataRequest(const rti::core::SampleIdentity &cookie, const TrainingAreaBase::SendFireSmokeMarkerDataRequest &request) override;
|
||||
|
||||
private:
|
||||
static Core::FunctionReplyObject fillFro(bool bSuccess);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Interface::Fts> m_ftsTaboc;
|
||||
std::shared_ptr<TrainingAreaBase::Interface::Fts> m_ftsTab;
|
||||
std::shared_ptr<SendToTab> m_txCallback;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Handle incoming SFM Telegrams and convert them to dds data topics
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-02-02
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "oc_telegrams.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class RecvFromTab
|
||||
{
|
||||
public:
|
||||
RecvFromTab() = default;
|
||||
virtual ~RecvFromTab() = default;
|
||||
|
||||
virtual void rxData(const OcTelegrams::Display &inData) = 0;
|
||||
virtual void rxData(const OcTelegrams::TimeSync &inData) = 0;
|
||||
virtual void rxData(const OcTelegrams::SpecificsExercise &inData) = 0;
|
||||
virtual void rxData(const OcTelegrams::SpecificsEquipment &inData) = 0;
|
||||
virtual void rxData(const OcTelegrams::TechnicalStateSfm &inData) = 0;
|
||||
virtual void rxData(const OcTelegrams::EnterLeaveAes &inData) = 0;
|
||||
virtual void rxData(const OcTelegrams::OwnPosition &inData) = 0;
|
||||
virtual void rxData(const TrainingAreaBase::TetraIcd::OpaquePayloadDataBase &inData) = 0;
|
||||
};
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* Handle incoming ETS SFM Telegrams and convert them to dds data topics
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
|
||||
#include "send_to_dds.h"
|
||||
|
||||
#include <QPointF>
|
||||
|
||||
#include "main_handler.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
SendToDds::SendToDds(MainHandler *mainHandler)
|
||||
: m_dtsTaboc(mainHandler->dds()->dtsTaboc())
|
||||
, m_dtsTab(mainHandler->dds()->dtsTab())
|
||||
, m_utmCoord(LtsCoordTrans::UtmCoordinate())
|
||||
, m_idToSend(1)
|
||||
, m_logger(getLogger("EtsRxDds"))
|
||||
{}
|
||||
|
||||
void SendToDds::rxData(const OcTelegrams::Display &inData)
|
||||
{
|
||||
if (0 == inData.multiplexer)
|
||||
{
|
||||
TrainingAreaBaseOC::ShortMessage outData;
|
||||
outData.id(m_idToSend++);
|
||||
|
||||
outData.textType(static_cast<long>(inData.textType));
|
||||
outData.indexVbg(inData.indexVbg);
|
||||
outData.indexVba(inData.indexVba);
|
||||
outData.timeStamp(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
else
|
||||
{
|
||||
TrainingAreaBaseOC::FreeText outData;
|
||||
outData.id(m_idToSend++);
|
||||
outData.text(inData.text.data());
|
||||
outData.timeStamp(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
}
|
||||
|
||||
void SendToDds::rxData(const OcTelegrams::TimeSync &inData)
|
||||
{
|
||||
TrainingAreaBaseOC::TimeSync outData;
|
||||
outData.id(1);
|
||||
|
||||
outData.gpsTime(inData.gpsTimeMsec);
|
||||
outData.timezone(inData.timezone);
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
|
||||
void SendToDds::rxData(const OcTelegrams::SpecificsExercise &inData)
|
||||
{
|
||||
m_utmCoord = LtsCoordTrans::UtmCoordinate::fromMgrs(QString::number(inData.zone) +
|
||||
QLatin1Char(inData.band) +
|
||||
QString::fromLatin1(inData.square) +
|
||||
QString::number(inData.northing).rightJustified(7, QLatin1Char('0')) +
|
||||
QString::number(inData.easting).rightJustified(7, QLatin1Char('0')));
|
||||
|
||||
TrainingAreaBaseOC::SpecificsExercise outData;
|
||||
outData.id(1);
|
||||
|
||||
outData.exerciseId(inData.exerciseId);
|
||||
outData.zone(inData.zone);
|
||||
outData.band(inData.band);
|
||||
outData.square(inData.square.data());
|
||||
outData.easting(inData.easting);
|
||||
outData.northing(inData.northing);
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
|
||||
void SendToDds::rxData(const OcTelegrams::SpecificsEquipment &inData)
|
||||
{
|
||||
TrainingAreaBaseOC::SpecificsEquipment outData;
|
||||
outData.id(1);
|
||||
|
||||
outData.equipmentNumber(inData.equipmentNumber);
|
||||
outData.playerType(inData.playerType);
|
||||
outData.language(static_cast<ProjectDataManagement::LanguageConfiguration::inner_enum>(inData.language));
|
||||
outData.pyrotechnics(inData.pyrotechnics);
|
||||
outData.voiceOutput(inData.voiceOutput);
|
||||
outData.exerciseBreak(inData.exerciseBreak);
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
|
||||
void SendToDds::rxData(const OcTelegrams::TechnicalStateSfm &inData)
|
||||
{
|
||||
TrainingAreaBaseOC::TechnicalStateSfm outData;
|
||||
outData.id(1);
|
||||
|
||||
outData.sfmConnected(inData.sfmConnected);
|
||||
outData.radioAvailable(inData.radioAvailable);
|
||||
outData.licensed(inData.licensed);
|
||||
outData.timeStamp(inData.gpsTimeMsec);
|
||||
outData.batteryTest(inData.batteryTest);
|
||||
outData.manipulationEquipment(inData.manipulationEquipment);
|
||||
outData.manipulationSfm(inData.manipulationSfm);
|
||||
outData.errorCode(inData.errorCode);
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
|
||||
void SendToDds::rxData(const OcTelegrams::EnterLeaveAes &inData)
|
||||
{
|
||||
TrainingAreaBaseOC::EnterLeaveAes outData;
|
||||
outData.id(1);
|
||||
|
||||
outData.aesId(inData.aesId);
|
||||
outData.aesEntered(inData.aesEntered);
|
||||
outData.abcArea(inData.nbcArea);
|
||||
outData.chargeTypeId(inData.chargeTasCode);
|
||||
outData.cAreaType(inData.cAreaType);
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
|
||||
void SendToDds::rxData(const OcTelegrams::OwnPosition &inData)
|
||||
{
|
||||
TrainingAreaBaseOC::OwnPosition outData;
|
||||
outData.id(1);
|
||||
|
||||
outData.valid(inData.valid);
|
||||
outData.timeStamp(inData.gpsTimeMsec); // gpstime is set to currentPartitionTime in canag_handler.cpp
|
||||
if (inData.valid)
|
||||
{
|
||||
const QPointF point(m_utmCoord.geographic(inData.easting, inData.northing));
|
||||
outData.position(Core::GeoPosition(point.x(), point.y(), 0));
|
||||
}
|
||||
else
|
||||
outData.position(Core::GeoPosition(nan(""), nan(""), 0));
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
}
|
||||
|
||||
void SendToDds::rxData(const TrainingAreaBase::TetraIcd::OpaquePayloadDataBase &inData)
|
||||
{
|
||||
switch (inData.payloadType)
|
||||
{
|
||||
case TrainingAreaBase::TetraIcd::OpaquePayloadDataBase::Type::OC:
|
||||
{
|
||||
TrainingAreaBase::OcData outData;
|
||||
outData.equipmentNumber(1);
|
||||
outData.payload(inData.payload);
|
||||
|
||||
m_dtsTab->publish(outData);
|
||||
break;
|
||||
}
|
||||
case TrainingAreaBase::TetraIcd::OpaquePayloadDataBase::Type::FireSmokeMarker:
|
||||
{
|
||||
TrainingAreaBase::FireSmokeMarkerData outData;
|
||||
outData.equipmentNumber(1);
|
||||
outData.payload(inData.payload);
|
||||
|
||||
m_dtsTab->publish(outData);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Received unhandled TrainingAreaBase::TetraIcd::OpaquePayloadDataBase::Type " << static_cast<int>(inData.payloadType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Handle incoming ETS SFM Telegrams and convert them to dds data topics
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "recv_from_tab.h"
|
||||
#include "dds_handler.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class MainHandler;
|
||||
class SendToDds : public RecvFromTab
|
||||
{
|
||||
public:
|
||||
explicit SendToDds(MainHandler *mainHandler);
|
||||
|
||||
void rxData(const OcTelegrams::Display &inData);
|
||||
void rxData(const OcTelegrams::TimeSync &inData);
|
||||
void rxData(const OcTelegrams::SpecificsExercise &inData);
|
||||
void rxData(const OcTelegrams::SpecificsEquipment &inData);
|
||||
void rxData(const OcTelegrams::TechnicalStateSfm &inData);
|
||||
void rxData(const OcTelegrams::EnterLeaveAes &inData);
|
||||
void rxData(const OcTelegrams::OwnPosition &inData);
|
||||
void rxData(const TrainingAreaBase::TetraIcd::OpaquePayloadDataBase &inData);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Interface::Dts> m_dtsTaboc;
|
||||
std::shared_ptr<TrainingAreaBase::Interface::Dts> m_dtsTab;
|
||||
|
||||
LtsCoordTrans::UtmCoordinate m_utmCoord; // for position translation to relative
|
||||
|
||||
int m_idToSend;
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
} // namespace TrainingAreaBase
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Send telegrams - interface
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-01-23
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "oc_telegrams.h"
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class SendToTab
|
||||
{
|
||||
protected:
|
||||
SendToTab() = default;
|
||||
virtual ~SendToTab() = default;
|
||||
|
||||
public:
|
||||
virtual bool txData(const OcTelegrams::DemandCyclicPositions &data) = 0;
|
||||
virtual bool txData(const OcTelegrams::TechnicalStateOc &data) = 0;
|
||||
virtual bool txData(const OcTelegrams::DemandSelftestSfm &data) = 0;
|
||||
virtual bool txData(const TrainingAreaBase::TetraIcd::OpaquePayloadDataBase &data) = 0;
|
||||
};
|
||||
}
|
||||
Executable
+124
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Settings for TabOc
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-02-09
|
||||
*/
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <ngltools/ngltools_filesystem.h>
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
QString Settings::portName()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("SerialPort/PortName"), QStringLiteral("COM6")).toString();
|
||||
}
|
||||
|
||||
quint32 Settings::baudrate()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("SerialPort/Baudrate"), 115200).toUInt();
|
||||
}
|
||||
|
||||
QString Settings::hostName()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("TcpPort/HostName"), QStringLiteral("localhost")).toString();
|
||||
}
|
||||
|
||||
quint32 Settings::port()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("TcpPort/Port"), 8006).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::portTtci()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("TcpPort/Port"), 8007).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::deviceId()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/DeviceId"), 30).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::manufacturerId()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/ManufacturerId"), 6).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::serialNumber()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/SerialNumber"), 0).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::verX()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/SW-Version-X__0..63"), 0).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::verY()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/SW-Version-Y__0..31"), 1).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::verZ()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/SW-Version-Z__0..26"), 0).toUInt();
|
||||
}
|
||||
|
||||
quint32 Settings::biteTimeOut()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/BITE-Timeout__0..65535"), 2000).toUInt();
|
||||
}
|
||||
|
||||
const QString Settings::baseCoord()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("Canag/BaseCoord"), QStringLiteral("32UMD00000000000000")).toString();
|
||||
}
|
||||
|
||||
void Settings::baseCoord(const QString &baseCoord)
|
||||
{
|
||||
return settings()->settings()->setValue(QLatin1String("Canag/BaseCoord"), QVariant(baseCoord));
|
||||
}
|
||||
|
||||
bool Settings::use2BytesForSingleFireDiameter()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("AES/use2BytesForSingleFireDiameter"), true).toBool();
|
||||
}
|
||||
|
||||
QString Settings::remoteServerHostname()
|
||||
{
|
||||
return settings()->settings()->value(QLatin1String("TabOC/remoteServerHostname"), QStringLiteral("EXCTDS01")).toString();
|
||||
}
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Settings for TabOc
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-02-09
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class Settings;
|
||||
}
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
namespace Settings
|
||||
{
|
||||
|
||||
//!< The real NGL::Settings - object
|
||||
NGL::Settings *settings();
|
||||
void setIniFile(const QString &ini);
|
||||
|
||||
// serial port configuration
|
||||
QString portName();
|
||||
quint32 baudrate();
|
||||
|
||||
// tcp port configuration
|
||||
QString hostName();
|
||||
quint32 port();
|
||||
quint32 portTtci();
|
||||
|
||||
// CANAG configuration
|
||||
quint32 deviceId();
|
||||
quint32 manufacturerId();
|
||||
quint32 serialNumber();
|
||||
quint32 verX();
|
||||
quint32 verY();
|
||||
quint32 verZ();
|
||||
quint32 biteTimeOut();
|
||||
const QString baseCoord();
|
||||
void baseCoord(const QString &baseCoord);
|
||||
|
||||
bool use2BytesForSingleFireDiameter();
|
||||
|
||||
QString remoteServerHostname();
|
||||
} // namespace Settings
|
||||
|
||||
} // namespace TrainingAreaBase
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
[SerialPort]
|
||||
;PortName=/dev/ttys0
|
||||
PortName=COM6
|
||||
Baudrate=115200
|
||||
|
||||
[Canag]
|
||||
DeviceId=30
|
||||
ManufacturerId=6
|
||||
;SerialNumber should be unique ...
|
||||
SerialNumber=0
|
||||
;SW-Version
|
||||
SW-Version-X__0..63=0
|
||||
SW-Version-Y__0..31=1
|
||||
BITE-Timeout__0..65535=2000
|
||||
;Base coord for current exercise
|
||||
BaseCoord=32UMD00000000000000
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Export definitions
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifndef TABOC_COMMON_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define TABOC_COMMON_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCTABOCCOMMON_LIB)
|
||||
# define TABOC_COMMON_EXPORT Q_DECL_EXPORT
|
||||
# ifndef WIN32
|
||||
# ifdef NDDSUSERDllExport
|
||||
# undef NDDSUSERDllExport
|
||||
# endif
|
||||
# define NDDSUSERDllExport Q_DECL_EXPORT
|
||||
# endif
|
||||
# else
|
||||
# define TABOC_COMMON_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef TABOC_CLIENT_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define TABOC_CLIENT_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCTABOCCLIENT_LIB)
|
||||
# define TABOC_CLIENT_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define TABOC_CLIENT_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef TABOC_SERVER_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define TABOC_SERVER_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_SVCTABOCSERVER_LIB)
|
||||
# define TABOC_SERVER_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define TABOC_SERVER_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<service namespace="TrainingAreaBaseOC" abbreviation="TABOC" procNames="TrainingAreaBaseOC" exerciseDependency="Exercise" groups="ExercisePreparation,ExerciseExecution,ExercisePaused" prio="">
|
||||
<rcs>
|
||||
<Id Id="$Id: ea_export_script.txt 41311 2018-04-23 09:43:45Z ehrlicher $"/>
|
||||
<HeadURL HeadURL="$HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Common/CodeGen/ea_export_script.txt $"/>
|
||||
<Author Author="$Author: ehrlicher $"/>
|
||||
</rcs>
|
||||
<tables/>
|
||||
<views/>
|
||||
<dbServiceStructs/>
|
||||
<topicTypes>
|
||||
<topicType name="CentralAesOverload" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="hasOverload" idlType="boolean"/>
|
||||
</topicType>
|
||||
<topicType name="ParticipantsOverload" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="hasOverload" idlType="boolean"/>
|
||||
</topicType>
|
||||
<topicType name="EnterLeaveAes" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="aesId" idlType="long"/>
|
||||
<column name="aesEntered" idlType="boolean"/>
|
||||
<column name="abcArea" idlType="char"/>
|
||||
<column name="chargeTypeId" idlType="long"/>
|
||||
<column name="cAreaType" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="FreeText" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="text" idlType="string"/>
|
||||
<column name="timeStamp" idlType="long long"/>
|
||||
</topicType>
|
||||
<topicType name="OcEquipmentBase">
|
||||
<column name="id" idlType="long" isKey="1"/>
|
||||
</topicType>
|
||||
<topicType name="OwnPosition" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="valid" idlType="boolean"/>
|
||||
<column name="timeStamp" idlType="long long"/>
|
||||
<column name="position" idlType="Core::GeoPosition"/>
|
||||
<column name="moutObjectId" idlType="long" isOptional="1"/>
|
||||
<column name="moutRoomId" idlType="long" isOptional="1"/>
|
||||
</topicType>
|
||||
<topicType name="ShortMessage" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="textType" idlType="long"/>
|
||||
<column name="indexVbg" idlType="long"/>
|
||||
<column name="indexVba" idlType="long"/>
|
||||
<column name="timeStamp" idlType="long long"/>
|
||||
</topicType>
|
||||
<topicType name="SpecificsEquipment" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="equipmentNumber" idlType="long"/>
|
||||
<column name="playerType" idlType="long"/>
|
||||
<column name="language" idlType="ProjectDataManagement::LanguageConfiguration"/>
|
||||
<column name="pyrotechnics" idlType="boolean"/>
|
||||
<column name="voiceOutput" idlType="boolean"/>
|
||||
<column name="exerciseBreak" idlType="boolean"/>
|
||||
</topicType>
|
||||
<topicType name="SpecificsExercise" isDataObject="1" autosubscribe="0" routeToClient="0" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="exerciseId" idlType="long"/>
|
||||
<column name="zone" idlType="long"/>
|
||||
<column name="band" idlType="char"/>
|
||||
<column name="square" idlType="string"/>
|
||||
<column name="easting" idlType="long"/>
|
||||
<column name="northing" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="TechnicalStateSfm" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="sfmConnected" idlType="boolean"/>
|
||||
<column name="radioAvailable" idlType="boolean"/>
|
||||
<column name="licensed" idlType="boolean"/>
|
||||
<column name="timeStamp" idlType="long"/>
|
||||
<column name="batteryTest" idlType="boolean"/>
|
||||
<column name="manipulationEquipment" idlType="boolean"/>
|
||||
<column name="manipulationSfm" idlType="boolean"/>
|
||||
<column name="errorCode" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="TimeSync" isDataObject="1" autosubscribe="0" routeToClient="0" routeToServer="0" routeInReplay="0" baseValueType="OcEquipmentBase">
|
||||
<column name="gpsTime" idlType="long long"/>
|
||||
<column name="timezone" idlType="char"/>
|
||||
</topicType>
|
||||
<topicType name="DemandCyclicPositionsRequest" extensibility="EXTENSIBLE">
|
||||
<column name="cycleTime" idlType="long"/>
|
||||
</topicType>
|
||||
<topicType name="DemandSfmSelfTestRequest" extensibility="EXTENSIBLE">
|
||||
<column name="dummy" idlType="char"/>
|
||||
</topicType>
|
||||
<topicType name="SendTechnicalStateOcRequest" extensibility="EXTENSIBLE">
|
||||
<column name="errorCode" idlType="long"/>
|
||||
</topicType>
|
||||
</topicTypes>
|
||||
<functionTopics>
|
||||
<functionTopic name="DemandCyclicPositions" requestTopicType="DemandCyclicPositionsRequest" replyTopicType="Core::FunctionReply" requestTopicName="DemandCyclicPositionsRequest" replyTopicName="DemandCyclicPositionsReply" routeInReplay="0"/>
|
||||
<functionTopic name="DemandSfmSelfTest" requestTopicType="DemandSfmSelfTestRequest" replyTopicType="Core::FunctionReply" requestTopicName="DemandSfmSelfTestRequest" replyTopicName="DemandSfmSelfTestReply" routeInReplay="0"/>
|
||||
<functionTopic name="SendTechnicalStateOc" requestTopicType="SendTechnicalStateOcRequest" replyTopicType="Core::FunctionReply" requestTopicName="SendTechnicalStateOcRequest" replyTopicName="SendTechnicalStateOcReply" routeInReplay="0"/>
|
||||
</functionTopics>
|
||||
<enumerations>
|
||||
<enumeration name="Dummy">
|
||||
<attribute key="NotUsed" value="0"/>
|
||||
</enumeration>
|
||||
</enumerations>
|
||||
</service>
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <memory>
|
||||
|
||||
#include "connection_handler.h"
|
||||
#include "settings.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int iRet = -1;
|
||||
NGLCoreApplication app(argc, argv);
|
||||
app.createLogger("TABOC", 0);
|
||||
|
||||
TrainingAreaBaseOC::Settings::setIniFile(QStringLiteral("taboc.dat"));
|
||||
|
||||
std::unique_ptr<TrainingAreaBaseOC::ConnectionHandler> conn(new TrainingAreaBaseOC::ConnectionHandler);
|
||||
if (conn->init())
|
||||
iRet = app.exec();
|
||||
|
||||
return iRet;
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TTCI-Server Library - handler for a specific TTCI channel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, NLS, 2019-01-17
|
||||
*/
|
||||
|
||||
#include "ttci_channel.h"
|
||||
|
||||
#include "ttci_device.h"
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
Channel::Channel(QChar channel, bool bIsBinary, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_channel(channel)
|
||||
, m_bIsBinary(bIsBinary)
|
||||
, m_device(nullptr)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("SCTT.Channel"))
|
||||
{}
|
||||
|
||||
void Channel::setDevice(Device *dev)
|
||||
{
|
||||
if (m_device)
|
||||
disconnect(m_device);
|
||||
m_device = dev;
|
||||
connect(dev, &Device::destroyed,
|
||||
this, &Channel::onDeviceDestroyed);
|
||||
connect(dev, &Device::dataAvailable,
|
||||
this, &Channel::onDataAvailable, Qt::QueuedConnection);
|
||||
connect(dev, &Device::disconnectedFromEq,
|
||||
this, &Channel::disconnectedFromEq);
|
||||
}
|
||||
|
||||
bool Channel::writeData(const QByteArray &data)
|
||||
{
|
||||
if (!m_device || !m_device->isOpen())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Error writing data - device is not open" ;
|
||||
return false;
|
||||
}
|
||||
// nicht schnell aber ok fuers erste
|
||||
QByteArray payload;
|
||||
if (m_bIsBinary)
|
||||
payload = data;
|
||||
else
|
||||
{
|
||||
payload.reserve(data.size());
|
||||
for (const char c : data)
|
||||
{
|
||||
if (c == 0x0a)
|
||||
continue;
|
||||
payload += c;
|
||||
}
|
||||
}
|
||||
return m_device->writeData(m_channel, payload);
|
||||
}
|
||||
|
||||
void Channel::onDeviceDestroyed()
|
||||
{
|
||||
m_device = nullptr;
|
||||
}
|
||||
|
||||
void Channel::onDataAvailable(const QByteArray &data)
|
||||
{
|
||||
TRACE_LOG(m_logger) << "Data available from "
|
||||
<< ", we are: " << m_channel
|
||||
<< ", data: '" << data << "'" ;
|
||||
emit dataAvailable(data);
|
||||
}
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TTCI-Server Library - handler for a specific TTCI channel
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, NLS, 2019-01-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
class Device;
|
||||
|
||||
// a single TTCI channel
|
||||
class Channel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Channel(QChar channel, bool bIsBinary, QObject *parent = nullptr);
|
||||
|
||||
void setDevice(Device *dev);
|
||||
|
||||
bool writeData(const QByteArray &data);
|
||||
|
||||
QChar channel() const { return m_channel; }
|
||||
|
||||
signals:
|
||||
void dataAvailable(const QByteArray &);
|
||||
void disconnectedFromEq();
|
||||
|
||||
|
||||
protected slots:
|
||||
void onDeviceDestroyed();
|
||||
void onDataAvailable(const QByteArray &data);
|
||||
|
||||
protected:
|
||||
QChar m_channel;
|
||||
bool m_bIsBinary;
|
||||
Device *m_device;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TTCI-Server Library
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, NLS, 2019-01-17
|
||||
*/
|
||||
|
||||
#include "ttci_device.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include <QByteArray>
|
||||
#include <QChar>
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
Device::Device(QObject *parent, bool tcpMode)
|
||||
: QObject(parent)
|
||||
, m_spQuerry(new LtsSerialPortQuerry::LtsSerialPortQuerry(this))
|
||||
, m_socket(new QTcpSocket(this))
|
||||
, m_restartTimer(new QTimer(this))
|
||||
, m_receiveTimeoutTimer(new QTimer(this))
|
||||
, m_tcpMode(tcpMode)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("TTCI.Device"))
|
||||
{
|
||||
connect(m_spQuerry, &LtsSerialPortQuerry::LtsSerialPortQuerry::connectionEstablished, this, &Device::open);
|
||||
m_spQuerry->restartQuerry();
|
||||
|
||||
m_receiveTimeoutTimer->setInterval(15000);
|
||||
connect(m_receiveTimeoutTimer, &QTimer::timeout, m_spQuerry, &LtsSerialPortQuerry::LtsSerialPortQuerry::continueQuerry);
|
||||
connect(m_receiveTimeoutTimer, &QTimer::timeout, this, &Device::disconnectedFromEq);
|
||||
|
||||
m_receiveTimeoutTimer->start();
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void Device::reOpen()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "ConnectionHandlerSerial::reOpen";
|
||||
close();
|
||||
QTimer::singleShot(2000, this, &Device::open);
|
||||
}
|
||||
|
||||
bool Device::open()
|
||||
{
|
||||
if (m_tcpMode)
|
||||
{
|
||||
m_restartTimer->stop();
|
||||
|
||||
close();
|
||||
m_socket = new QTcpSocket(this);
|
||||
m_socket->connectToHost(Settings::hostName(), Settings::portTtci());
|
||||
if (!m_socket->waitForConnected())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Could not (re)open tcp destination " << m_socket->peerName() << ":" << m_socket->peerPort() << ", error: " << m_socket->errorString();
|
||||
return false;
|
||||
}
|
||||
connect(m_socket, &QTcpSocket::readyRead, this, &Device::onDataAvailable);
|
||||
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &Device::handleError);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &Device::open);
|
||||
DEBUG_LOG(m_logger) << "(Re)openend tcp destination " << m_socket->peerName() << ":" << Settings::portTtci();
|
||||
}
|
||||
else
|
||||
{
|
||||
//use portscanner
|
||||
m_serialPort = m_spQuerry->currentSerialPort();
|
||||
DEBUG_LOG(m_logger) << "(Re)openend serial destination " << m_serialPort->portName() << ":";
|
||||
|
||||
connect(m_serialPort, &QSerialPort::readyRead, this, &Device::onDataAvailable);
|
||||
|
||||
}
|
||||
m_receiveTimeoutTimer->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::close()
|
||||
{
|
||||
if (m_socket)
|
||||
{
|
||||
if (m_socket->isOpen())
|
||||
m_socket->close();
|
||||
DEBUG_LOG(m_logger) << "Closing TCP Port";
|
||||
delete m_socket;
|
||||
m_socket = nullptr;
|
||||
m_receiveTimeoutTimer->stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::isOpen() const
|
||||
{
|
||||
if (m_tcpMode)
|
||||
return m_socket->isOpen();
|
||||
else
|
||||
return m_serialPort->isOpen();
|
||||
}
|
||||
|
||||
void Device::onDataAvailable()
|
||||
{
|
||||
if (m_tcpMode)
|
||||
m_buffer += m_socket->readAll();
|
||||
else
|
||||
m_buffer += m_serialPort->readAll();
|
||||
|
||||
parseBuffer();
|
||||
}
|
||||
|
||||
void Device::parseBuffer()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Buffer - size: " << m_buffer.size()
|
||||
<< ", data: " << m_buffer.toHex() ;
|
||||
// TTCI-Frame: 02 <len> <data> <crc> 03
|
||||
// data: <destination> <payload>
|
||||
// nicht gefunden oder letztes Byte -> ignore
|
||||
int idxStart = -1;
|
||||
while ((idxStart = m_buffer.indexOf(0x02)) >= 0)
|
||||
{
|
||||
TRACE_LOG(m_logger) << "idxStart: " << idxStart ;
|
||||
// Laengenbyte im Buffer?
|
||||
int idxLen = idxStart + 1;
|
||||
if (idxLen >= m_buffer.size())
|
||||
return;
|
||||
|
||||
unsigned char len = static_cast<unsigned char>(m_buffer.at(idxLen));
|
||||
TRACE_LOG(m_logger) << "idxLen: " << idxLen
|
||||
<< ", len: " << len
|
||||
<< " = 0x" << QByteArray::number(len, 16);
|
||||
int idxCrc = idxLen + len + 1;
|
||||
int idxEnd = idxCrc + 1;
|
||||
if (idxEnd >= m_buffer.size())
|
||||
return;
|
||||
TRACE_LOG(m_logger) << "idxCrc: " << idxCrc
|
||||
<< ", idxEnd: " << idxEnd ;
|
||||
if (idxEnd >= m_buffer.size())
|
||||
return;
|
||||
|
||||
// korrekten Ende-Marker gefunden?
|
||||
if (m_buffer.at(idxEnd) != 0x03)
|
||||
{
|
||||
// nein -> neu aufsetzen
|
||||
WARNING_LOG(m_logger) << "Could not find valid end marker - searching new start marker"
|
||||
;
|
||||
m_buffer = m_buffer.mid(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char crc = static_cast<unsigned char>(m_buffer.at(idxCrc));
|
||||
unsigned char crcCalc = calcChecksum(m_buffer.mid(idxStart, idxCrc - idxStart));
|
||||
TRACE_LOG(m_logger) << "crc: " << crc
|
||||
<< ", calculated: " << crcCalc
|
||||
;
|
||||
if (crc != crcCalc)
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Invalid checksum - is: " << crcCalc
|
||||
<< " - should be: " << crc ;
|
||||
}
|
||||
|
||||
unsigned char destination = static_cast<unsigned char>(m_buffer.at(idxLen + 1));
|
||||
QByteArray payload = m_buffer.mid(idxLen + 2, len - 1);
|
||||
|
||||
if (len == 255 || !m_payload.isEmpty())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "telegram with more than 254 bytes ..." ;
|
||||
// Erstes Telegram -> destination merken
|
||||
if (len == 255 && m_payload.isEmpty())
|
||||
DEBUG_LOG(m_logger) << "Remember the payload destination: " << destination ;
|
||||
m_payload += payload;
|
||||
if (len != 255)
|
||||
{
|
||||
// Letztes Telegramm -> ausgeben
|
||||
DEBUG_LOG(m_logger) << "destination: " << destination
|
||||
<< ", data: " << m_payload.toHex() ;
|
||||
emit dataAvailable(m_payload);
|
||||
m_payload.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// normales Telegramm -> ausgeben
|
||||
DEBUG_LOG(m_logger) << "destination: " << destination
|
||||
<< ", data: " << payload.toHex() ;
|
||||
emit dataAvailable(payload);
|
||||
}
|
||||
|
||||
m_buffer = m_buffer.mid(idxEnd + 1);
|
||||
m_receiveTimeoutTimer->start();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::writeData(const QChar destination, const QByteArray &payload)
|
||||
{
|
||||
|
||||
if ((m_tcpMode && !m_socket->isOpen()) || (!m_tcpMode && !m_serialPort->isOpen()))
|
||||
return false;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Writing " << payload.size()
|
||||
<< " Bytes to channel " << destination ;
|
||||
// in TTCI-Frames verpacken, nicht schnell aber fuer die paar Bytes sollte es ok sein
|
||||
QByteArray data = payload;
|
||||
do
|
||||
{
|
||||
QByteArray payloadToWrite = data.left(254);
|
||||
|
||||
QByteArray buf;
|
||||
buf.reserve(payloadToWrite.size() + 5);
|
||||
|
||||
buf.append(0x02);
|
||||
buf.append(static_cast<unsigned char>(payloadToWrite.size() + 1));
|
||||
buf.append(destination.toLatin1());
|
||||
buf.append(payloadToWrite);
|
||||
buf.append(calcChecksum(buf));
|
||||
buf.append(0x03);
|
||||
|
||||
DEBUG_LOG(m_logger) << "Writing: " << buf.toHex() ;
|
||||
if (m_tcpMode)
|
||||
m_socket->write(buf);
|
||||
else
|
||||
m_serialPort->write(buf);
|
||||
|
||||
data = data.mid(254);
|
||||
|
||||
}
|
||||
while (!data.isEmpty());
|
||||
|
||||
if(m_tcpMode)
|
||||
m_socket->flush();
|
||||
else
|
||||
m_serialPort->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char Device::calcChecksum(const QByteArray &checksumData)
|
||||
{
|
||||
unsigned char sum = 0;
|
||||
for (const char c : checksumData)
|
||||
sum ^= static_cast<unsigned char>(c);
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void Device::handleError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Spontaneous error " << error << " doesn't have to be an error ...";
|
||||
|
||||
const QString errStr(QStringLiteral("QTcpSocket::SocketError ") + QString::number(error) +
|
||||
QStringLiteral(" from tcp port ") + QString::number(m_socket->peerPort()) +
|
||||
QStringLiteral(" error: ") + m_socket->errorString());
|
||||
|
||||
switch (error)
|
||||
{
|
||||
case QTcpSocket::SocketError::ConnectionRefusedError:
|
||||
case QTcpSocket::SocketError::SocketAccessError:
|
||||
case QTcpSocket::SocketError::SocketResourceError:
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Closing port trying to reopen it.";
|
||||
close();
|
||||
m_restartTimer->start();
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
case QTcpSocket::SocketError::RemoteHostClosedError:
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Host closed trying to reopen.";
|
||||
m_restartTimer->start();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
ERROR_LOG(m_logger) << errStr;
|
||||
close();
|
||||
m_restartTimer->start();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Device::handleSerialError(QSerialPort::SerialPortError error)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Spontaneous error " << error << " doesn't have to be an error ...";
|
||||
|
||||
const QString errStr(QStringLiteral("QSerialPort::SerialPortError ") + QString::number(error) +
|
||||
QStringLiteral(" error: ") + m_serialPort->errorString());
|
||||
|
||||
switch (error)
|
||||
{
|
||||
case QSerialPort::SerialPortError::OpenError:
|
||||
case QSerialPort::SerialPortError::NotOpenError:
|
||||
case QSerialPort::SerialPortError::DeviceNotFoundError:
|
||||
case QSerialPort::SerialPortError::PermissionError:
|
||||
case QSerialPort::SerialPortError::WriteError:
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Closing port trying to reopen it.";
|
||||
m_restartTimer->start();
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
default:
|
||||
{
|
||||
WARNING_LOG(m_logger) << errStr;
|
||||
m_restartTimer->start();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} //namespace TrainingAreaBaseOC
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TTCI-Server Library
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Patric Karwat, NLS, 2019-01-17
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QTcpSocket>
|
||||
#include <QSerialPort>
|
||||
#include <LtsSerialPortQuerry/lts_serial_port_querry.h>
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace TrainingAreaBaseOC
|
||||
{
|
||||
|
||||
// a connection to a PU over TTCI
|
||||
class Device : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Device(QObject *parent = nullptr, bool tcpMode = false);
|
||||
virtual ~Device();
|
||||
|
||||
bool close();
|
||||
|
||||
QString portName() const;
|
||||
QTcpSocket *port() { return m_socket; }
|
||||
bool isOpen() const;
|
||||
|
||||
bool writeData(const QChar destination, const QByteArray &payload);
|
||||
|
||||
signals:
|
||||
void dataAvailable(const QByteArray &payload);
|
||||
void disconnectedFromEq();
|
||||
|
||||
public slots:
|
||||
void onDataAvailable();
|
||||
void reOpen();
|
||||
bool open();
|
||||
void handleError(QAbstractSocket::SocketError error);
|
||||
void handleSerialError(QSerialPort::SerialPortError error);
|
||||
|
||||
protected:
|
||||
void parseBuffer();
|
||||
static unsigned char calcChecksum(const QByteArray &checksumData);
|
||||
private:
|
||||
LtsSerialPortQuerry::LtsSerialPortQuerry *m_spQuerry = nullptr;
|
||||
QTcpSocket *m_socket = nullptr;
|
||||
QSerialPort *m_serialPort = nullptr;
|
||||
QByteArray m_buffer;
|
||||
QByteArray m_payload;
|
||||
QTimer *m_restartTimer = nullptr;
|
||||
QTimer *m_receiveTimeoutTimer = nullptr;
|
||||
int m_errCnt = 0;
|
||||
bool m_tcpMode = false;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
#include "websocket_handler.h"
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <QPointF>
|
||||
#include <QTimer>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
namespace TrainingAreaBaseOC {
|
||||
|
||||
WebsocketHandler::WebsocketHandler(QObject *parent, int exerciseId,
|
||||
const std::shared_ptr<Interface::Dts> dtsTabOc, const std::shared_ptr<Interface::Dtc> dtcTabOc)
|
||||
: QObject(parent)
|
||||
, m_exerciseId(exerciseId)
|
||||
, m_dtsTaboc(dtsTabOc)
|
||||
, m_dtcTaboc(dtcTabOc)
|
||||
, m_logger(getLogger("WebSocketHandler"))
|
||||
{
|
||||
m_tryConnectionTimer = new QTimer(this);
|
||||
connect(m_tryConnectionTimer, &QTimer::timeout, this, &WebsocketHandler::onTryConnectionTimerTimeout);
|
||||
m_tryConnectionTimer->start(10 * 1000);
|
||||
connect(m_dtcTaboc.get(), &TrainingAreaBaseOC::Interface::Dtc::specificsEquipmentChanged,
|
||||
this, [this](){
|
||||
m_ownEquipmentId = m_dtcTaboc->specificsEquipment(1).equipmentNumber();
|
||||
});
|
||||
m_ownEquipmentId = m_dtcTaboc->specificsEquipment(1).equipmentNumber();
|
||||
|
||||
|
||||
m_fetchPositionTimer = new QTimer(this);
|
||||
connect(m_fetchPositionTimer, &QTimer::timeout, this, &WebsocketHandler::onFetchPositionTimerTimeout);
|
||||
|
||||
m_socket = new QWebSocket(QString(), QWebSocketProtocol::Version13, this);
|
||||
|
||||
connect(m_socket, &QWebSocket::stateChanged,
|
||||
this, &WebsocketHandler::onNewSocketState);
|
||||
connect(m_socket, &QWebSocket::textMessageReceived,
|
||||
this, &WebsocketHandler::onNewTextMessageFromSocket);
|
||||
|
||||
}
|
||||
|
||||
void WebsocketHandler::onTryConnectionTimerTimeout()
|
||||
{
|
||||
if (m_socket->state() != QAbstractSocket::UnconnectedState)
|
||||
m_socket->disconnect();
|
||||
connectToServer();
|
||||
|
||||
|
||||
// try connecting to servicemanagement for ports
|
||||
}
|
||||
|
||||
void WebsocketHandler::onFetchPositionTimerTimeout()
|
||||
{
|
||||
sendPositionRequest();
|
||||
}
|
||||
|
||||
void WebsocketHandler::onNewSocketState(QAbstractSocket::SocketState state)
|
||||
{
|
||||
|
||||
if (m_tryConnectionTimer->isActive())
|
||||
{
|
||||
//do the connection stuff here
|
||||
switch (state)
|
||||
{
|
||||
case QAbstractSocket::ConnectedState:
|
||||
m_state = WebSocketState::CONNECTEDTOSERVER;
|
||||
sendServicePortRequest();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (m_state == WebSocketState::CONNECTTOPPM)
|
||||
{
|
||||
//do the simple fetch stuff here
|
||||
switch(state)
|
||||
{
|
||||
case QAbstractSocket::ConnectedState:
|
||||
m_state = WebSocketState::CONNECTEDTOPPM;
|
||||
m_fetchPositionTimer->start(3 * 1000);
|
||||
break;
|
||||
case QAbstractSocket::UnconnectedState:
|
||||
m_state = WebSocketState::DISCONNECTED;
|
||||
m_tryConnectionTimer->start();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (m_state == WebSocketState::CONNECTEDTOPPM)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case QAbstractSocket::UnconnectedState:
|
||||
case QAbstractSocket::ClosingState:
|
||||
m_fetchPositionTimer->stop();
|
||||
m_tryConnectionTimer->start();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebsocketHandler::onNewTextMessageFromSocket(const QString &message)
|
||||
{
|
||||
|
||||
if (m_state == WebSocketState::CONNECTEDTOSERVER)
|
||||
parseServices(message);
|
||||
if(m_state == WebSocketState::CONNECTEDTOPPM)
|
||||
onSendOwnPosition(message);
|
||||
|
||||
}
|
||||
|
||||
void WebsocketHandler::connectToExPPM(int exPort)
|
||||
{
|
||||
m_socket->open(QUrl(QStringLiteral("ws://")+Settings::remoteServerHostname()+QStringLiteral(":")+ QString::number(exPort)));
|
||||
}
|
||||
|
||||
void WebsocketHandler::sendPositionRequest()
|
||||
{
|
||||
if (m_ownEquipmentId == 0)
|
||||
return;
|
||||
m_socket->sendTextMessage(QStringLiteral("{\"type\": \"ocPositionData\",\"uuid\": \"7777\", \"data\":{ \"equipmentNr\":")+ QString::number(m_ownEquipmentId) + QStringLiteral("} }"));
|
||||
}
|
||||
|
||||
void WebsocketHandler::onSendOwnPosition(const QString &message)
|
||||
{
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
|
||||
if (!doc.isObject())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "No Valid Object in " << message;
|
||||
return;
|
||||
}
|
||||
if (doc.object().value(QStringLiteral("data")).toObject().isEmpty())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "No Data Array in" << message;
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonObject obj = doc.object().value(QStringLiteral("data")).toObject();
|
||||
|
||||
TrainingAreaBaseOC::OwnPosition outData;
|
||||
outData.id(1);
|
||||
|
||||
//outData.valid(inData.valid);
|
||||
// outData.timeStamp(inData.gpsTimeMsec); // gpstime is set to currentPartitionTime in canag_handler.cpp
|
||||
if (obj.contains(QStringLiteral("lon")) && obj.contains(QStringLiteral("lat")))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Got message with position";
|
||||
outData.position(Core::GeoPosition(obj.value(QStringLiteral("lon")).toDouble(),obj.value(QStringLiteral("lat")).toDouble(),obj.value(QStringLiteral("height")).toDouble()));
|
||||
outData.timeStamp(QDateTime::currentMSecsSinceEpoch());
|
||||
if (obj.contains(QStringLiteral("buildingId")) && obj.contains(QStringLiteral("roomId")) )
|
||||
{
|
||||
outData.moutObjectId().set(obj.value(QStringLiteral("buildingId")).toInt());
|
||||
outData.moutRoomId().set(obj.value(QStringLiteral("roomId")).toInt());
|
||||
}
|
||||
}
|
||||
else
|
||||
outData.position(Core::GeoPosition(nan(""), nan(""),0));
|
||||
|
||||
m_dtsTaboc->publish(outData);
|
||||
|
||||
}
|
||||
|
||||
void WebsocketHandler::connectToServer()
|
||||
{
|
||||
//send service port request like obd
|
||||
DEBUG_LOG(m_logger) << "Try connect to servicemanagement " << QUrl(QStringLiteral("ws://")+Settings::remoteServerHostname()+QStringLiteral(":")+ QString::number(7300)).toString();
|
||||
m_socket->open(QUrl(QStringLiteral("ws://")+Settings::remoteServerHostname()+QStringLiteral(":")+ QString::number(7300)));
|
||||
}
|
||||
|
||||
void WebsocketHandler::sendServicePortRequest()
|
||||
{
|
||||
m_socket->sendTextMessage(QStringLiteral("{\"type\": \"servicePortsInformationRequest\",\"uuid\": \"7777\"}"));
|
||||
DEBUG_LOG(m_logger) << "Wait For Services Reply";
|
||||
}
|
||||
|
||||
void WebsocketHandler::parseServices(const QString &message)
|
||||
{
|
||||
m_tryConnectionTimer->stop();
|
||||
|
||||
DEBUG_LOG(m_logger) << "Received SERVICES MESSAGE: " << message;
|
||||
m_socket->close();
|
||||
int exPort = -1;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
|
||||
if (!doc.isObject())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "No Valid Object in " << message;
|
||||
return;
|
||||
}
|
||||
if (!doc.object().value(QStringLiteral("data")).isArray())
|
||||
{
|
||||
WARNING_LOG(m_logger) << "No Data Array in" << message;
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonArray exercises = doc.object().value(QStringLiteral("data")).toArray();
|
||||
QJsonArray services;
|
||||
|
||||
for (const QJsonValue &exercise : exercises)
|
||||
{
|
||||
const QJsonObject &exe = exercise.toObject();
|
||||
if (exe.value(QStringLiteral("exerciseId")).toInt(-1) == m_exerciseId)
|
||||
{
|
||||
services = exe.value(QStringLiteral("services")).toArray();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const QJsonValue &service : qAsConst(services))
|
||||
{
|
||||
const QJsonObject &obj = service.toObject();
|
||||
|
||||
if (obj.value(QStringLiteral("name")).toString() == QStringLiteral("ParticipantManagement"))
|
||||
{
|
||||
const QJsonArray ports = obj.value(QStringLiteral("ports")).toArray();
|
||||
for (const QJsonValue &port : ports)
|
||||
{
|
||||
if (port.toObject().value(QStringLiteral("typeId")).toInt() == 1)
|
||||
exPort = port.toObject().value(QStringLiteral("number")).toInt(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exPort > 0)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Connect To ppm ON: " << exPort;
|
||||
connectToExPPM(exPort);
|
||||
m_state = WebSocketState::CONNECTTOPPM;
|
||||
}
|
||||
else
|
||||
m_tryConnectionTimer->start();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace TrainingAreaBaseOC
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "dds_handler.h"
|
||||
#include "logger.h"
|
||||
#include <taboc_dtc.h>
|
||||
#include <QWebSocket>
|
||||
|
||||
class QTimer;
|
||||
|
||||
namespace TrainingAreaBaseOC {
|
||||
|
||||
enum class WebSocketState {
|
||||
DISCONNECTED = 0, // should not happen
|
||||
CONNECTEDTOSERVER, // 1
|
||||
CONNECTTOPPM, // 2
|
||||
CONNECTEDTOPPM, // 3
|
||||
};
|
||||
|
||||
class WebsocketHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebsocketHandler(QObject *parent, int exerciseId,
|
||||
const std::shared_ptr<Interface::Dts> dtsTabOc, const std::shared_ptr<Interface::Dtc> dtcTabOc);
|
||||
|
||||
private slots:
|
||||
void onTryConnectionTimerTimeout();
|
||||
void onFetchPositionTimerTimeout();
|
||||
void onNewSocketState(QAbstractSocket::SocketState state);
|
||||
void onNewTextMessageFromSocket(const QString &message);
|
||||
|
||||
private:
|
||||
void connectToExPPM(int exPort);
|
||||
void sendPositionRequest();
|
||||
void onSendOwnPosition(const QString &message);
|
||||
|
||||
void connectToServer();
|
||||
void sendServicePortRequest();
|
||||
void parseServices(const QString &message);
|
||||
|
||||
WebSocketState m_state = WebSocketState::DISCONNECTED;
|
||||
int m_exerciseId = 0;
|
||||
int m_ownEquipmentId = 0;
|
||||
std::shared_ptr<Interface::Dts> m_dtsTaboc;
|
||||
std::shared_ptr<Interface::Dtc> m_dtcTaboc;
|
||||
QTimer *m_tryConnectionTimer = nullptr;
|
||||
QTimer *m_fetchPositionTimer = nullptr;
|
||||
QWebSocket *m_socket = nullptr;
|
||||
|
||||
LOGGER_T &m_logger;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace TrainingAreaBaseOc
|
||||
Reference in new issue
Block a user