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

@@ -0,0 +1,36 @@
#
# Write all incoming / outgoing telegrams to a socket for PrtCnv (xdr encoded)
# $Id$
# $HeadURL$
# $Author$
#
SET(SRCS
convert_from_json.cpp
convert_from_json.h
convert_to_json.cpp
convert_to_json.h
socket_dispatcher_json.cpp
socket_dispatcher_json.h
plugin.cpp
plugin.h
playerIds.dat
PrtCnv.dat
)
QT5_ADD_RESOURCES(SRCS tetra.qrc)
NGL_ADD_LIBRARY(AAR SV tabPluginTetraJson MODULE ${SRCS})
NGL_INSTALL_TARGET(tabPluginTetraJson)
NGL_TARGET_LINK_LIBRARIES(tabPluginTetraJson
CoPrtCnv
NGLLogger
nglsettings
Qt5::Core
Qt5::Network
Qt5::Sql
Qt5::WebSockets
tabPluginInterface
tabTelegrams
PrtCnvLib
)
+102
View File
@@ -0,0 +1,102 @@
[Common]
LogCentralTelegrams=1
LogRadioTelegrams=1
LogTelegramPath=logdata
LogTelegramPrefix=PrtCnv_
; virtual TeilnehmerId of protocol-converter
VirtualIdPrtCnv=90
MaxEqID=50000
;or max unsigned short: 65535
[Db]
DatabaseFileName=db/telegrams.db
[Central]
UseSocketsInsteadFiles=1
HostName=EXCTDS01
Port=9600
; Milliseconds
AckTimeout=900
ConnectTimeout=2000
DisconnectTimeout=10000
AliveTimeout=3000
; nbr of unsuccessful send-attempts, before telegram will be deleted without sending it
NbrOfAttempts=5
[Radio]
UseScrambling=1
EmergencyCallISSI=9000
BroadcastISSI=5000
ConnectTimeout=2000
HostName=CCSTSW01
Port=52010
HostName1=CCSTSW02
Port1=52010
HostISSI=9998
; Seconds
CyclicEnvironmentTime=30
HostISSI=9999
; Milliseconds
DisconnectTimeout=10000
AliveTimeout=3000
; 1 - UseSDS_TL
UseSDS_TL=0
DumpReceivedTelegrams=1
DumpSentTelegrams=1
; GSSIs which we would like to subscribe to - make sure to not subscribe to our own BroadcastISSI !
ListenGroups=5001
[Login]
LoginFilePathWithPrefix=login/pk_
DoLicenseDespiteSelftestOrConfigFailure=0
DoCheckPlayerId=1
LowestPlayerId=500
HighestPlayerId=65535
PlayerIdFilePath=playerIds.dat
[Broadcast]
; Count, how often a Broadcast telegram will be send (minimum 1, 0 will be ignored and the telegram is sent anyway)
MaxBroadcastSendCount=1
; In seconds, delay between sending 1st and 2nd broadcast (< 0 -> disable)
BroadcastDelay2=10
; In seconds, delay between sending 2nd and 3rd broadcast (< 0 -> disable)
BroadcastDelay3=20
; the time to wait for NachforderungBroadcast from TNEs, before begin sending the broadcasts
ClaimWindow=30
; factor for extend the claimwindow when we have full load in downlink
FullLoadFactor=3
; if less TNEs ask for broadcasts, they will be send directed
BroadcastLimit=10
; the time to wait before sending next broadcast in milliseconds
SendGap=500
; minimum id of DUS-Mines to be able to get reactivated
MinimumDusMineId=2048
[TrafficControl]
; from PrtCnv to Tetra (Telegram-Counting)
; Number of telegrams (0..255) reported by air-interface-report telegram when we have high load
SendQueueHighLoad=150
; Number of telegrams that will be sent per second, when we have high-load
SendTelegramsHighLoad=50
; Number of telegrams (0..255) reported by air-interface-report telegram when we have full load
SendQueueFullLoad=200
; Number of telegrams that will be sent per second, when we have full-load
SendTelegramsFullLoad=10
; from Tetra to PrtCnv (Byte-Counting)
; high load in percent (0 .. 100)
ReceiveQueueHighLoad=50
; full load in percent (ReceiveQueueHighLoad .. 100)
ReceiveQueueFullLoad=75
; hysteresis in percent (0 .. 10)
ReceiveQueueHysteresis=5
; Averaging time in seconds
ReceiveAveragingTime=5
; Throughput in KB/s per Transceiver
ReceiveThroughput=3.6
; Transceiver count
TransceiverCount=6
[Injection]
Enable=true
Port=46666
@@ -0,0 +1,199 @@
/**
* Read XDR telegrams from a directory and convert it to EtsTetraIcd structures
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-14
*/
#include "convert_from_json.h"
#include <TrainingAreaBase/training_area_telegrams.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace TetraRadio
{
ConvertFromJson::ConvertFromJson(const std::shared_ptr<TrainingAreaBase::RecvFromTab> &rxHandler,
const char *loggerName)
: m_rxHandler(rxHandler)
, m_bWarnedOnce(false)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName))
{}
bool ConvertFromJson::handleTelegram(const QByteArray &baJson, int &recvCounter)
{
using namespace TrainingAreaBase::TetraIcd;
if (baJson.isEmpty())
return false;
const QJsonObject obj = QJsonDocument::fromJson(baJson).object();
const QJsonValue sendCounter = obj.value(QStringLiteral("counter"));
if (!sendCounter.isUndefined())
{
if (!sendCounter.isDouble())
return false;
recvCounter = sendCounter.toInt(-1);
DEBUG_LOG(m_logger) << "Incoming telegram with Id " << obj.value(QStringLiteral("type")).toString();
if (recvCounter < 0)
return false;
}
const QJsonValue typeId = obj.value(QStringLiteral("typeId"));
if (!typeId.isDouble())
{
ERROR_LOG(m_logger) << "Can not read type id: " << baJson;
return false;
}
const QJsonObject data = obj.value(QStringLiteral("data")).toObject();
try
{
switch (typeId.toInt())
{
case Constraints<Acknowledge>::id(): // 0
{
auto ack = TrainingAreaBase::TetraIcd::Acknowledge::fromJson(data);
handleAcknowledge(ack.timeStamp, ack.ackCounter);
break;
}
case Constraints<ManipulationReport>::id(): // 2
rxData(ManipulationReport::fromJson(data));
break;
case Constraints<EquipmentOperationalState>::id(): // 7
rxData(EquipmentOperationalState::fromJson(data));
break;
case Constraints<EquipmentConfigurationReport>::id(): // 11
rxData(EquipmentConfigurationReport::fromJson(data));
break;
case Constraints<CtaDataError>::id(): // 12
rxData(CtaDataError::fromJson(data));
break;
case Constraints<RestoreRadioReadiness>::id(): // 13
rxData(RestoreRadioReadiness::fromJson(data));
break;
case Constraints<PositionReport>::id(): // 21
rxData(PositionReport::fromJson(data));
break;
case Constraints<PositionReportWithMountedEquipments>::id(): // 22
rxData(PositionReportWithMountedEquipments::fromJson(data));
break;
case Constraints<MineTriggered>::id(): // 40
rxData(MineTriggered::fromJson(data));
break;
case Constraints<DefinitionIed>::id(): // 43
rxData(DefinitionIed::fromJson(data));
break;
case Constraints<StateIed>::id(): // 44
rxData(StateIed::fromJson(data));
break;
case Constraints<DusSingleMineState>::id(): // 45
rxData(DusSingleMineState::fromJson(data));
break;
case Constraints<StateChangeDirect>::id(): // 52
rxData(StateChangeDirect::fromJson(data));
break;
case Constraints<StateChangeIndirect>::id(): // 53
rxData(StateChangeIndirect::fromJson(data));
break;
case Constraints<OperatorAction>::id(): // 55
rxData(OperatorAction::fromJson(data));
break;
case Constraints<SelectiveReammunition>::id(): // 56
rxData(SelectiveReammunition::fromJson(data));
break;
case Constraints<StateChange>::id(): // 61
rxData(StateChange::fromJson(data));
break;
case Constraints<FunctionalAreaEntry>::id(): // 63
rxData(FunctionalAreaEntry::fromJson(data));
break;
case Constraints<OpaquePayloadDataIn>::id(): // 71
rxData(OpaquePayloadDataIn::fromJson(data));
break;
case Constraints<SpecialSensorData>::id(): // 92
rxData(SpecialSensorData::fromJson(data));
break;
case Constraints<MinelayerTrailerData>::id(): // 95
rxData(MinelayerTrailerData::fromJson(data));
break;
case Constraints<MinelayerThrowerData>::id(): // 96
rxData(MinelayerThrowerData::fromJson(data));
break;
case Constraints<MinelayerTrailerPosition>::id(): // 97
rxData(MinelayerTrailerPosition::fromJson(data));
break;
case Constraints<EquipmentEvent>::id(): // 185
rxData(EquipmentEvent::fromJson(data));
break;
case Constraints<EquipmentGearAssignment>::id(): // 200
rxData(EquipmentGearAssignment::fromJson(data));
break;
case Constraints<EquipmentGearChargeState>::id(): // 202
rxData(EquipmentGearChargeState::fromJson(data));
break;
case Constraints<BiteReport>::id(): // 205
rxData(BiteReport::fromJson(data));
break;
case Constraints<EquipmentGearEvent>::id(): // 210
rxData(EquipmentGearEvent::fromJson(data));
break;
case Constraints<ParticipantSwVersions>::id(): // 213
rxData(ParticipantSwVersions::fromJson(data));
break;
case Constraints<ParticipantModeReport>::id(): // 231
rxData(ParticipantModeReport::fromJson(data));
break;
case Constraints<ParticipantConfigurationReport>::id(): // 233
rxData(ParticipantConfigurationReport::fromJson(data));
break;
case Constraints<SanitationRepair>::id(): //24
rxData(SanitationRepair::fromJson(data));
break;
case Constraints<SanitaryMaterialStockReport>::id(): //241
rxData(SanitaryMaterialStockReport::fromJson(data));
break;
case Constraints<Sanitation>::id(): //242
rxData(Sanitation::fromJson(data));
break;
case Constraints<EmergencyCall>::id(): // 1120
rxData(EmergencyCall::fromJson(data));
break;
case Constraints<ApacheLaserDesignation>::id():
rxData(ApacheLaserDesignation::fromJson(data));
break;
case Constraints<ProxyConnectionState>::id(): // 2013
handleTetraConnectionChanged(ProxyConnectionState::fromJson(data).connectedToSystem);
break;
case Constraints<ParticipantLicenseChange>::id(): // 2014
//participantLicenseChange(ParticipantLicenseChange::fromJson(data));
break;
default:
WARNING_LOG(m_logger) << "Handler for telegram Id " << typeId.toInt()
<< " not yet implemented (" << baJson << ")";
return false;
}
}
catch (const std::exception &e)
{
WARNING_LOG(m_logger) << "Could not parse json: " << e.what() << " (" << baJson << ")";
return false;
}
return true;
}
template<typename T>
void ConvertFromJson::rxData(const T &data)
{
if (!m_rxHandler)
{
if (!m_bWarnedOnce)
{
ERROR_LOG(m_logger) << "No RecvFromTab available";
m_bWarnedOnce = true;
}
return;
}
return m_rxHandler->rxData(data);
}
} // namespace TetraRadio
@@ -0,0 +1,40 @@
/**
* Read XDR telegrams from a directory and convert it to EtsTetraIcd structures
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-14
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include <TrainingAreaBase/recv_from_tab.h>
#include <memory>
class XDR_Wrapper_it;
namespace TetraRadio
{
class ConvertFromJson
{
public:
explicit ConvertFromJson(const std::shared_ptr<TrainingAreaBase::RecvFromTab> &rxHandler, const char *loggerName);
virtual ~ConvertFromJson() = default;
protected:
bool handleTelegram(const QByteArray &baJson, int &recvCounter);
virtual void handleAcknowledge(qint64 timeStamp, int counter) = 0;
virtual void handleTetraConnectionChanged(bool bConnected) = 0;
private:
template<typename T>
void rxData(const T &data);
private:
// helper for convertBite
static TrainingAreaBase::TetraIcd::BiteError convertBiteError(const QJsonObject &obj);
std::shared_ptr<TrainingAreaBase::RecvFromTab> m_rxHandler;
protected:
bool m_bWarnedOnce;
NGL::INGLLogger &m_logger;
};
} // namespace TetraRadio
@@ -0,0 +1,293 @@
/**
* Write XDR telegrams to a directory
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-14
*/
#include "convert_to_json.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QJsonDocument>
#include <QJsonObject>
namespace TetraRadio
{
ConvertToJson::ConvertToJson(const char *loggerName)
: QObject()
, SendToTab()
, m_bConnected(false)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName))
{
resetSendCounter();
}
ConvertToJson::~ConvertToJson()
{}
TrainingAreaBase::SendToTab::HandleState ConvertToJson::canHandleTelegram(int equipmentNumber, int telegramId) const
{
if (!m_bConnected)
return HandleState::No;
using namespace TrainingAreaBase::TetraIcd;
if (telegramId == Constraints<ExerciseEquipmentAssignmentComplete>::id() ||
telegramId == Constraints<ExerciseStateData>::id())
return HandleState::YesShare;
Q_UNUSED(equipmentNumber)
return HandleState::YesFallback;
}
void ConvertToJson::resetSendCounter()
{
m_sendCounter = 1; // start at 1
}
bool ConvertToJson::sendTele(const QByteArray &ba)
{
const QJsonDocument doc(QJsonDocument::fromJson(ba));
QJsonObject obj = doc.object();
const QJsonValue typeId = obj[QStringLiteral("typeId")];
if (typeId.toInt() == TrainingAreaBase::TetraIcd::TelegramId::BESTAETIGUNG)
{
NOTICE_LOG(m_logger) << "Outgoing ack";
emit outgoingMessage(ba);
return true;
}
obj[QStringLiteral("counter")] = m_sendCounter++;
const QByteArray baToSend = QJsonDocument(obj).toJson(QJsonDocument::Compact);
NOTICE_LOG(m_logger) << "Outgoing message: " << typeId.toInt();
emit outgoingMessage(baToSend);
return true;
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::Acknowledge &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::DemandBite &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::Init &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::SetVulnerabilityModel &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::DemandEquipmentConfigurationReport &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::DemandVulnerabilityStateChange &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::LanguageSelection &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::EquipmentMessage &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::EquipmentMessageOC &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CreateSingleExplosion &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CreateAreaExplosion &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CreateMineArea &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::MineAreaSingleMineState &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CreateNContamination &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CreateCContamination &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::DeactivateEffectSimulation &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ModifyAreaEffectSimulationName &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::PauseEffectSimulation &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ResumeEffectSimulation &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::MineTriggered &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::DusMineReactivation &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CreateFunctionalArea &data)
{
return sendToTx(&data);
}
bool TetraRadio::ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CreateDusSingleMine &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ExerciseEnvironmentData &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ExerciseStateData &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ParticipantSwVersionsRequest &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::OpaquePayloadDataOut &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::TechnicalArea &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CesHitInformation &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::CesHitRequest &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ParticipantMode &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::DemandParticipantMode &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ParticipantConfiguration &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::DemandParticipantConfiguration &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::ExerciseEquipmentAssignmentComplete &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::Sanitation &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::SelectiveReammunition &data)
{
DEBUG_LOG(m_logger) << "ConvertToJson::txData SelectiveReammunition";
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::SetDevicePowerRequest &data)
{
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::OcCommand &data)
{
DEBUG_LOG(m_logger) << "ConvertToJson::txData OcCommand";
return sendToTx(&data);
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::UAVPerformMission &data)
{
Q_UNUSED(data);
return false;
}
bool ConvertToJson::txData(const TrainingAreaBase::TetraIcd::UAVChangeState &data)
{
Q_UNUSED(data);
return false;
}
bool ConvertToJson::sendToTx(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *data)
{
if (!m_bConnected)
return false;
QJsonObject obj;
obj[QStringLiteral("data")] = data->toJson();
obj[QStringLiteral("type")] = data->type();
obj[QStringLiteral("typeId")] = data->id();
if (data->id() != TrainingAreaBase::TetraIcd::TelegramId::BESTAETIGUNG)
obj[QStringLiteral("counter")] = m_sendCounter++;
const QByteArray ba = QJsonDocument(obj).toJson(QJsonDocument::Compact);
if (data->id() != TrainingAreaBase::TetraIcd::TelegramId::BESTAETIGUNG)
DEBUG_LOG(m_logger) << "Sending tele: " << ba;
emit outgoingMessage(ba);
return true;
}
} // namespace TetraRadio
@@ -0,0 +1,87 @@
/**
* Write XDR telegrams to a directory
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-14
*/
#pragma once
#include <QtCore/QObject>
#include <NGLLogger/INGLLogger.h>
#include <QWebSocket>
#include <TrainingAreaBase/send_to_tab.h>
namespace TetraRadio
{
class ConvertToJson final : public QObject, public TrainingAreaBase::SendToTab
{
Q_OBJECT
public:
explicit ConvertToJson(const char *loggerName);
~ConvertToJson();
const char *name() const override { return "Tetra"; }
HandleState canHandleTelegram(int equipmentNumber, int telegramId) const override;
void resetSendCounter() override;
bool sendTele(const QByteArray &ba);
void setConnected(bool bConnected) { m_bConnected = bConnected; }
public:
bool txData(const TrainingAreaBase::TetraIcd::Acknowledge &data) override;
bool txData(const TrainingAreaBase::TetraIcd::DemandBite &data) override;
bool txData(const TrainingAreaBase::TetraIcd::Init &data) override;
bool txData(const TrainingAreaBase::TetraIcd::SetVulnerabilityModel &data) override;
bool txData(const TrainingAreaBase::TetraIcd::DemandEquipmentConfigurationReport &data) override;
bool txData(const TrainingAreaBase::TetraIcd::DemandVulnerabilityStateChange &data) override;
bool txData(const TrainingAreaBase::TetraIcd::LanguageSelection &data) override;
bool txData(const TrainingAreaBase::TetraIcd::EquipmentMessage &data) override;
bool txData(const TrainingAreaBase::TetraIcd::EquipmentMessageOC &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CreateSingleExplosion &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CreateAreaExplosion &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CreateMineArea &data) override;
bool txData(const TrainingAreaBase::TetraIcd::MineAreaSingleMineState &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CreateNContamination &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CreateCContamination &data) override;
bool txData(const TrainingAreaBase::TetraIcd::DeactivateEffectSimulation &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ModifyAreaEffectSimulationName &data) override;
bool txData(const TrainingAreaBase::TetraIcd::PauseEffectSimulation &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ResumeEffectSimulation &data) override;
bool txData(const TrainingAreaBase::TetraIcd::MineTriggered &data) override;
bool txData(const TrainingAreaBase::TetraIcd::DusMineReactivation &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CreateFunctionalArea &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CreateDusSingleMine &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ExerciseEnvironmentData &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ExerciseStateData &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ParticipantSwVersionsRequest &data) override;
bool txData(const TrainingAreaBase::TetraIcd::OpaquePayloadDataOut &data) override;
bool txData(const TrainingAreaBase::TetraIcd::TechnicalArea &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CesHitInformation &data) override;
bool txData(const TrainingAreaBase::TetraIcd::CesHitRequest &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ParticipantMode &data) override;
bool txData(const TrainingAreaBase::TetraIcd::DemandParticipantMode &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ParticipantConfiguration &data) override;
bool txData(const TrainingAreaBase::TetraIcd::DemandParticipantConfiguration &data) override;
bool txData(const TrainingAreaBase::TetraIcd::ExerciseEquipmentAssignmentComplete &data) override;
bool txData(const TrainingAreaBase::TetraIcd::Sanitation &data) override;
bool txData(const TrainingAreaBase::TetraIcd::SelectiveReammunition &data) override;
bool txData(const TrainingAreaBase::TetraIcd::SetDevicePowerRequest &data) override;
bool txData(const TrainingAreaBase::TetraIcd::OcCommand &data) override;
bool txData(const TrainingAreaBase::TetraIcd::UAVPerformMission &data) override;
bool txData(const TrainingAreaBase::TetraIcd::UAVChangeState &) override ;
bool txData(const TrainingAreaBase::TetraIcd::ExFireReadiness &) override { return false; };
bool txData(const TrainingAreaBase::TetraIcd::ExFiredReport &) override { return false; };
bool txData(const TrainingAreaBase::TetraIcd::ExConfirmationReport &) override { return false; };
bool sendToTx(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *data);
Q_SIGNALS:
void outgoingMessage(const QByteArray &msg);
protected:
protected:
int m_sendCounter;
bool m_bConnected;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1 @@
1-65535
+239
View File
@@ -0,0 +1,239 @@
/**
* Send / Receive telegrams to/from PrtCnv
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-09-26
*/
#include "plugin.h"
#include "NGLLogger/INGLLogger.h"
#include "socket_dispatcher_json.h"
#include <nglsettings/nglsettings_connections.h>
#include <QCoreApplication>
#include <QFile>
#include <QSqlDatabase>
#include <QJsonDocument>
#include <QSettings>
#include <ngldb/dbcommon.h>
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
#include <QSettings>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <qglobal.h>
namespace TetraRadio
{
//
// TetraXdrPlugin
//
std::shared_ptr<TrainingAreaBase::Plugin::Interface> TetraJsonPlugin::createInterface(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb,
const std::shared_ptr<TrainingAreaBase::TxMixin> &,
const std::shared_ptr<TrainingAreaBase::DdsHandler> &,
NGL::Settings *settings)
{
return std::make_shared<TetraJsonInterfaceImpl>(pdp, recvFromTabCb, settings);
}
TetraJsonInterfaceImpl::TetraJsonInterfaceImpl(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb,
NGL::Settings *settings)
: m_socketDispatcher(new SocketDispatcher(pdp, recvFromTabCb))
, m_pdp(pdp)
, m_prtCnvApp(nullptr)
, m_exerciseId(0)
, m_radioGrpId(0)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("TAB.Plugin.TetraJsonInterfaceImpl"))
{
N_CoPrtCnv::CoPrtCnvTelegramConverter::setUse2BytesForSingleFireDiameter(
settings->settings()->value(QLatin1String("AES/use2BytesForSingleFireDiameter"), true).toBool());
N_CoPrtCnv::CoPrtCnvTelegramConverter::setUsePvSan(
settings->settings()->value(QLatin1String("TrainingAreaBase/UsePvSan"), false).toBool());
ERROR_LOG(m_logger) << "hello i got this!";
}
TetraJsonInterfaceImpl::~TetraJsonInterfaceImpl()
{
cleanupProcess();
}
bool TetraJsonInterfaceImpl::init()
{
m_exerciseId = m_pdp->exerciseId();
NOTICE_LOG(m_logger) << "Our exercise id is " << m_exerciseId;
if (m_exerciseId == 0) // its sufficient to check only in exercise 0
if (!checkConfigTemplates())
return false;
return updatePrtCnv();
}
void TetraJsonInterfaceImpl::exerciseChanged()
{
NOTICE_LOG(m_logger) << "TetraJsonInterfaceImpl::exerciseChanged";
if (m_pdp->exerciseRadioGroup().id() != m_radioGrpId) // radio grp changed
if (!updatePrtCnv())
ERROR_LOG(m_logger) << "Error changing PrtCnv state from radio grp id: "
<< m_radioGrpId
<< " to " << m_pdp->exerciseRadioGroup().id();
}
bool TetraJsonInterfaceImpl::updatePrtCnv()
{
int newRadioGrpId = m_pdp->exerciseRadioGroup().id();
if (newRadioGrpId == m_radioGrpId) //nothing changed for PrtCnv
{
NOTICE_LOG(m_logger) << "No need to update PrtCnv configuration - radio group did not change: " << m_radioGrpId;
return true;
}
NOTICE_LOG(m_logger) << "Our radio group id is " << newRadioGrpId;
if (newRadioGrpId > 0)
{
// Create Prtcnv.dat with specific config
QFile::remove(iniFileOut());
if (!QFile::copy(iniFileIn(), iniFileOut()))
{
ERROR_LOG(m_logger) << "Error creating ini file for PrtCnv: " << iniFileOut();
return false;
}
ProjectDataManagement::RadioGroup radioGrp = m_pdp->exerciseRadioGroup();
{
QSettings s(iniFileOut(), QSettings::IniFormat);
s.setValue(QStringLiteral("Common/LogTelegramPath"), QStringLiteral("logdata/ex_%1").arg(m_exerciseId));
s.setValue(QStringLiteral("Common/LogTelegramPrefix"), QStringLiteral("PrtCnv_ex_%1").arg(m_exerciseId));
s.setValue(QStringLiteral("Central/HostName"), QStringLiteral("directConnection")); // direct connection thorugh signals/slots
s.setValue(QStringLiteral("Radio/HostName"), QString::fromStdString(radioGrp.radioHostname()));
s.setValue(QStringLiteral("Radio/Port"), radioGrp.radioPort());
s.setValue(QStringLiteral("Radio/HostName1"), QString::fromStdString(radioGrp.radioBackupHostname()));
s.setValue(QStringLiteral("Radio/Port1"), radioGrp.radioBackupPort());
s.setValue(QStringLiteral("Radio/HostISSI"), radioGrp.issi());
s.setValue(QStringLiteral("Radio/BroadcastISSI"), radioGrp.group_issi1());
s.setValue(QStringLiteral("Radio/ListenGroups"), radioGrp.group_issi2());
s.setValue(QStringLiteral("Radio/UseScrambling"), m_pdp->useTetraScrambling() ? 1 : 0);
s.setValue(QStringLiteral("Injection/Port"), m_socketDispatcher->randomListenPort());
NOTICE_LOG(m_logger) << "TetraJson Injection/Port = " << s.value(QStringLiteral("Injection/Port")).toInt();
s.sync();
}
// Start PrtCnv
if (!startPrtCnv())
return false;
}
else // new radio grp ID is 0 and PrtCnv has ben running
cleanupProcess(); // quit running PrtCnv
// set radio grp at the end of update process
m_radioGrpId = newRadioGrpId;
return true;
}
bool TetraJsonInterfaceImpl::checkConfigTemplates()
{
if (!QFile::exists(iniFileIn()))
{
INFO_LOG(m_logger) << "Creating " << iniFileIn();
if (!QFile::copy(QStringLiteral(":/PrtCnv.dat"), iniFileIn()))
{
ERROR_LOG(m_logger) << "Error creating " << iniFileIn();
return false;
}
else
QFile(iniFileIn()).setPermissions(QFile::ReadOwner | QFile::ReadGroup | QFile::ReadOther | QFile::WriteOwner);
}
if (!QFile::exists(playerIdsFile()))
{
INFO_LOG(m_logger) << "Creating " << playerIdsFile();
if (!QFile::copy(QStringLiteral(":/playerIds.dat"), playerIdsFile()))
{
ERROR_LOG(m_logger) << "Error creating " << playerIdsFile();
return false;
}
else
QFile(playerIdsFile()).setPermissions(QFile::ReadOwner | QFile::ReadGroup | QFile::ReadOther | QFile::WriteOwner);
}
return true;
}
bool TetraJsonInterfaceImpl::startPrtCnv()
{
NOTICE_LOG(m_logger) << "Starting PrtCnv (intern)";
cleanupProcess();
m_prtCnvApp = new PrtCnv::PrtCnvApplication;
const QSqlDatabase db = m_pdp->databaseConnection();
m_prtCnvApp->setDatabaseConnectionParams(db.driverName(),
db.hostName(),
db.databaseName(),
m_pdp->databaseSchemaName(),
db.userName(),
db.password());
m_prtCnvApp->setExerciseId(m_pdp->exerciseId());
m_prtCnvApp->setConfigFile(iniFileOut());
m_prtCnvApp->startApp();
m_socketDispatcher->init(m_prtCnvApp);
onTetraConnectionChanged(false); // initial
connect(m_socketDispatcher.get(), &SocketDispatcher::tetraConnectionChanged,
this, &TetraJsonInterfaceImpl::onTetraConnectionChanged);
connect(m_socketDispatcher.get(), &SocketDispatcher::warehouseMessage,
this, &TetraJsonInterfaceImpl::directMessage);
return true;
}
std::shared_ptr<TrainingAreaBase::SendToTab> TetraJsonInterfaceImpl::sendToTab()
{
return m_socketDispatcher->sendToTab();
}
bool TetraJsonInterfaceImpl::canSendData() const
{
return m_socketDispatcher->canSendData() && m_prtCnvApp != nullptr;
}
void TetraJsonInterfaceImpl::onTetraConnectionChanged(bool bConnected)
{
emit connectionState(QString::fromUtf8(name()), bConnected);
}
void TetraJsonInterfaceImpl::cleanupProcess()
{
m_socketDispatcher->init(nullptr);
if (!m_prtCnvApp)
return;
m_prtCnvApp->shutdownApp();
delete m_prtCnvApp;
m_prtCnvApp = nullptr;
onTetraConnectionChanged(false);
}
QString TetraJsonInterfaceImpl::iniFileIn()
{
return iniPath() + QStringLiteral("/PrtCnv.dat");
}
QString TetraJsonInterfaceImpl::iniFileOut() const
{
return iniPath() + QStringLiteral("/PrtCnv_ex_%1.dat").arg(m_exerciseId);
}
QString TetraJsonInterfaceImpl::iniPath()
{
return QCoreApplication::applicationDirPath() + QLatin1Char('/');
}
QString TetraJsonInterfaceImpl::playerIdsFile()
{
return iniPath() + QStringLiteral("/playerIds.dat");
}
} // namespace TetraRadio
+73
View File
@@ -0,0 +1,73 @@
/**
* Send / Receive telegrams to/from PrtCnv
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-09-26
*/
#pragma once
#include <QObject>
#include <TrainingAreaBase/tab_plugin_interface.h>
#include <memory>
#include <NGLLogger/INGLLogger.h>
#include <PrtCnv/PrtCnvApplication.hpp>
namespace TetraRadio
{
class TetraJsonPlugin : public QObject, public TrainingAreaBase::Plugin::Plugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "special:3:tab.jsonsocket")
Q_INTERFACES(TrainingAreaBase::Plugin::Plugin)
public:
TetraJsonPlugin() = default;
~TetraJsonPlugin() = default;
std::shared_ptr<TrainingAreaBase::Plugin::Interface> createInterface(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb,
const std::shared_ptr<TrainingAreaBase::TxMixin> &sendToTabCb,
const std::shared_ptr<TrainingAreaBase::DdsHandler> &,
NGL::Settings *settings) override;
};
class SocketDispatcher;
class TetraJsonInterfaceImpl final : public TrainingAreaBase::Plugin::Interface
{
Q_OBJECT
public:
TetraJsonInterfaceImpl(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb,
NGL::Settings *settings);
~TetraJsonInterfaceImpl();
bool init() override;
std::shared_ptr<TrainingAreaBase::SendToTab> sendToTab() override;
bool isDebugInterface() const override
{
return false;
}
bool canSendData() const override;
void exerciseChanged() override;
const char *name() const override { return "TETRA"; }
private Q_SLOTS:
void onTetraConnectionChanged(bool bConnected);
private:
void cleanupProcess();
static QString iniFileIn();
QString iniFileOut() const;
static QString iniPath();
static QString playerIdsFile();
bool updatePrtCnv();
bool checkConfigTemplates();
bool startPrtCnv();
private:
std::unique_ptr<SocketDispatcher> m_socketDispatcher;
TrainingAreaBase::Plugin::DataProvider *m_pdp;
PrtCnv::PrtCnvApplication *m_prtCnvApp;
int m_exerciseId;
int m_radioGrpId;
NGL::INGLLogger &m_logger;
};
} // namespace TetraRadio
@@ -0,0 +1,142 @@
/**
* Send / Receive telegrams to/from PrtCnv
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-09-26
*/
#include "socket_dispatcher_json.h"
#include <QCoreApplication>
#include <QDir>
#include <QTcpServer>
namespace TetraRadio
{
//
// SocketDispatcher
//
SocketDispatcher::SocketDispatcher(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb)
: QObject()
, ConvertFromJson(recvFromTabCb, "TAB.Plugin.SocketDispatcher")
, m_txHandler(std::make_shared<ConvertToJson>("TxSocketHandler"))
, m_dataProvider(pdp)
, m_prtCnvApp(nullptr)
, m_ackTimer(new QTimer(this))
, m_bInitialAck(false)
, m_receiveCounter(0)
{
m_ackTimer->setInterval(1000);
m_ackTimer->setSingleShot(false);
connect(m_ackTimer, &QTimer::timeout,
this, &SocketDispatcher::onAckTimeout);
}
SocketDispatcher::~SocketDispatcher()
{
delete m_ackTimer;
}
bool SocketDispatcher::init(PrtCnv::PrtCnvApplication *prtCnvApp)
{
if (m_prtCnvApp == prtCnvApp)
return true;
if (m_prtCnvApp)
m_prtCnvApp->disconnect(this);
m_prtCnvApp = prtCnvApp;
if (m_prtCnvApp)
{
connect(m_txHandler.get(), &ConvertToJson::outgoingMessage,
m_prtCnvApp, &PrtCnv::PrtCnvApplication::textMessageFromCentralToPrtCnv);
connect(m_prtCnvApp, &PrtCnv::PrtCnvApplication::textMessageFromPrtCnvToCentral,
this, &SocketDispatcher::onTextMessageReceived);
connect(m_prtCnvApp, &PrtCnv::PrtCnvApplication::warehouseMessageFromPtrCnv,
this, &SocketDispatcher::warehouseMessage);
m_ackTimer->start();
m_bInitialAck = true;
}
else
{
m_bInitialAck = false;
m_ackTimer->stop();
}
return true;
}
quint16 SocketDispatcher::randomListenPort() const
{
QTcpServer *s = new QTcpServer;
s->listen(QHostAddress::Any, 0);
while (!s->isListening())
QCoreApplication::processEvents();
quint16 port = s->serverPort();
delete s;
return port;
}
std::shared_ptr<TrainingAreaBase::SendToTab> SocketDispatcher::sendToTab()
{
return std::static_pointer_cast<TrainingAreaBase::SendToTab>(m_txHandler);
}
void SocketDispatcher::onTextMessageReceived(const QByteArray &message)
{
DEBUG_LOG(m_logger) << "TetraJson onTextMessageReceived: '" << message << "'";
int newRecvCounter = 0;
if (!handleTelegram(message, newRecvCounter))
return;
m_receiveCounter = newRecvCounter;
}
void SocketDispatcher::handleTetraConnectionChanged(bool bConnected)
{
emit tetraConnectionChanged(bConnected);
}
void SocketDispatcher::handleAcknowledge(qint64 timeStamp, int counter)
{
TRACE_LOG(m_logger) << "Ack from Socket for time " << timeStamp
<< " with counter " << counter
<< ", initial: " << m_bInitialAck;
if (m_bInitialAck)
{
m_txHandler->setConnected(true);
m_bInitialAck = false;
onSendInitialData();
}
// TODO: remove all now outdated telegrams !?
}
void SocketDispatcher::onAckTimeout()
{
TrainingAreaBase::TetraIcd::Acknowledge ack;
ack.timeStamp = time(nullptr) * 1000;
ack.ackCounter = m_receiveCounter;
m_txHandler->txData(ack);
}
void SocketDispatcher::onSendInitialData()
{
if (m_dataProvider)
{
NOTICE_LOG(m_logger) << "sending initial data exerciseId=" << m_dataProvider->exerciseId();
// on connect we must send:
if (m_dataProvider->exerciseId() > 0)
{
// - Umgebungsmeldung
m_txHandler->txData(m_dataProvider->exerciseEnvironmentData());
}
// - Betriebszustandsmeldung
m_txHandler->txData(m_dataProvider->exerciseStateData());
m_txHandler->txData(m_dataProvider->exerciseEquipmentAssignment());
const auto &aesTeles = m_dataProvider->aesTelegrams();
for (TrainingAreaBase::TetraIcd::TrainingAreaTelegram *aesTele : aesTeles)
m_txHandler->sendToTx(aesTele);
}
}
} // namespace TetraRadio
@@ -0,0 +1,54 @@
/**
* Send / Receive telegrams to/from PrtCnv
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-09-26
*/
#pragma once
#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
#include <QTimer>
#include <TrainingAreaBase/tab_plugin_interface.h>
#include <PrtCnv/PrtCnvApplication.hpp>
#include "convert_from_json.h"
#include "convert_to_json.h"
namespace TetraRadio
{
class SocketDispatcher final : public QObject, protected ConvertFromJson
{
Q_OBJECT
public:
explicit SocketDispatcher(TrainingAreaBase::Plugin::DataProvider *pdp,
const std::shared_ptr<TrainingAreaBase::RecvFromTab> &recvFromTabCb);
~SocketDispatcher();
bool init(PrtCnv::PrtCnvApplication *prtCnvApp);
std::shared_ptr<TrainingAreaBase::SendToTab> sendToTab();
quint16 randomListenPort() const;
bool canSendData() const { return m_prtCnvApp != nullptr; }
Q_SIGNALS:
void tetraConnectionChanged(bool bConnected);
void warehouseMessage(const QByteArray &jsonData);
private Q_SLOTS:
void onTextMessageReceived(const QByteArray &message);
void onAckTimeout();
void onSendInitialData();
private:
void disconnectSocket(QWebSocket *s);
void handleTetraConnectionChanged(bool bConnected) override;
void handleAcknowledge(qint64 timeStamp, int counter) override;
private:
std::shared_ptr<ConvertToJson> m_txHandler;
TrainingAreaBase::Plugin::DataProvider *m_dataProvider;
PrtCnv::PrtCnvApplication *m_prtCnvApp;
QTimer *m_ackTimer;
bool m_bInitialAck;
int m_receiveCounter;
};
} // namespace TetraRadio
+6
View File
@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>playerIds.dat</file>
<file>PrtCnv.dat</file>
</qresource>
</RCC>