Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+231
View File
@@ -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()
File diff suppressed because it is too large. Load diff
+220
View File
@@ -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
View File
@@ -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
View File
@@ -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
+54
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+88
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+38
View File
@@ -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
+24
View File
@@ -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
+42
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+44
View File
@@ -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
+26
View File
@@ -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;
};
}
+124
View File
@@ -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
+52
View File
@@ -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
+16
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+21
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+67
View File
@@ -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
View File
@@ -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
View File
@@ -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