/** * Send / Receive telegrams to/from PrtCnv * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2016-09-26 */ #include "socket_dispatcher_json.h" #include #include #include namespace TetraRadio { // // SocketDispatcher // SocketDispatcher::SocketDispatcher(TrainingAreaBase::Plugin::DataProvider *pdp, const std::shared_ptr &recvFromTabCb) : QObject() , ConvertFromJson(recvFromTabCb, "TAB.Plugin.SocketDispatcher") , m_txHandler(std::make_shared("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 SocketDispatcher::sendToTab() { return std::static_pointer_cast(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