FlightGear next
cpdlc.cxx
Go to the documentation of this file.
1
2//
3// cpdlc.cxx
4//
5// started November 2020
6// Authors: Michael Filhol, Henning Stahlke
7//
8// This program is free software; you can redistribute it and/or
9// modify it under the terms of the GNU General Public License as
10// published by the Free Software Foundation; either version 2 of the
11// License, or (at your option) any later version.
12//
13// This program is distributed in the hope that it will be useful, but
14// WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software
20// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21//
22// $Id$
23//
25
26#include "cpdlc.hxx"
27#include <Main/fg_props.hxx>
28
29const std::string CPDLC_IRC_SERVER {"mpirc.flightgear.org"};
30const std::string CPDLC_MSGPREFIX_CONNECT {"___CPDLC_CONNECT___"};
31const std::string CPDLC_MSGPREFIX_MSG {"___CPDLC_MSG___"};
32const std::string CPDLC_MSGPREFIX_DISCONNECT {"___CPDLC_DISCONNECT___"};
33
35// CPDLCManager
37
39 _irc(irc)
40{
41 // CPDLC link status props
42 _pStatus = fgGetNode("/network/cpdlc/link/status", true);
43 _pStatus->setIntValue(_status);
44 _pDataAuthority = fgGetNode("/network/cpdlc/link/data-authority", true);
45 _pDataAuthority->setStringValue(_data_authority);
46 // CPDLC message output
47 _pMessage = fgGetNode("/network/cpdlc/rx/message", true);
48 _pMessage->setStringValue("");
49 _pNewMessage = fgGetNode("/network/cpdlc/rx/new-message", true);
50 _pNewMessage->setBoolValue(0);
51}
52
56
57/*
58connect will be called by
591) user via a fgcommand defined in multiplaymgr
602) CPDLC::update() while waiting for IRC to become ready
61
62authority parameter is accepted only when coming from disconnected state,
63disconnect must be called first if user wants to change authority (however, ATC
64handover is not blocked by this)
65
66IRC connection will be initiated if necessary, it takes a while to connect to IRC
67which is why connect is called from update() when in state CPDLC_WAIT_IRC_READY
68*/
69bool CPDLCManager::connect(const std::string authority = "")
70{
71 // ensure we get an authority on first call but do not accept a change before
72 // resetting _data_authority in disconnect()
73 if (_data_authority.empty()) {
74 if (authority.empty()) {
75 SG_LOG(SG_NETWORK, SG_WARN, "cpdlcConnect not possible: empty argument!");
76 return false;
77 } else {
78 _data_authority = authority;
79 }
80 }
81 if (!authority.empty() && authority != _data_authority) {
82 SG_LOG(SG_NETWORK, SG_WARN, "cpdlcConnect: cannot change authority now, use disconnect first!");
83 return false;
84 }
85
86 // launch IRC connection as needed
87 if (!_irc->isConnected()) {
88 SG_LOG(SG_NETWORK, SG_INFO, "Connecting to IRC server...");
89 if (!_irc->login()) {
90 SG_LOG(SG_NETWORK, SG_WARN, "IRC login failed.");
91 return false;
92 }
93 _status = CPDLC_WAIT_IRC_READY;
94 return true;
95 } else if (_irc->isReady() && _status != CPDLC_ONLINE) {
96 SG_LOG(SG_NETWORK, SG_INFO, "CPDLC sending 'connect'");
97 _status = CPDLC_CONNECTING;
98 _pStatus->setIntValue(_status);
99 return _irc->sendPrivmsg(_data_authority, CPDLC_MSGPREFIX_CONNECT);
100 }
101 return false;
102}
103
105{
106 if (_irc && _irc->isConnected() && !_data_authority.empty()) {
107 _irc->sendPrivmsg(_data_authority, CPDLC_MSGPREFIX_DISCONNECT);
108 }
109 _data_authority = "";
110 _pDataAuthority->setStringValue(_data_authority);
111 _status = CPDLC_OFFLINE;
112 _pStatus->setIntValue(_status);
113}
114
115bool CPDLCManager::send(const std::string message)
116{
117 std::string textline(CPDLC_MSGPREFIX_MSG); // TODO surely a way to format std string in line
118 textline += ' ';
119 textline += message;
120 return _irc->sendPrivmsg(_data_authority, textline);
121}
122
123// move next message from input queue to property tree
125{
126 if (!_incoming_messages.empty()) {
127 struct IRCMessage entry = _incoming_messages.front();
128 _incoming_messages.pop_front();
129 _pMessage->setStringValue(entry.textline.substr(CPDLC_MSGPREFIX_MSG.length() + 1));
130 if (_incoming_messages.empty()) {
131 _pNewMessage->setBoolValue(0);
132 }
133 }
134}
135
136/*
137 update() call this regularly to complete connect and check for new messages
138 update frequency requirement should be low (e.g. once per second) but frame rate
139 will not hurt as the code is slim
140*/
142{
143 if (_irc) {
144 if (_irc->isConnected()) {
145 if (_status == CPDLC_WAIT_IRC_READY && _irc->isReady()) {
146 SG_LOG(SG_NETWORK, SG_INFO, "CPDLC IRC ready, connecting...");
147 connect();
148 }
149 if (_irc->hasMessage()) {
150 processMessage(_irc->getMessage());
151 }
152 }
153 // IRC disconnected (unexpectedly)
154 else if (_status != CPDLC_OFFLINE) {
155 _status = CPDLC_OFFLINE;
156 _pStatus->setIntValue(_status);
157 }
158 }
159}
160
161// process incoming message
162void CPDLCManager::processMessage(struct IRCMessage message)
163{
164 // connection accepted by ATC, or new data authority (been transferred)
165 if (message.textline.find(CPDLC_MSGPREFIX_CONNECT) == 0) {
166 SG_LOG(SG_NETWORK, SG_INFO, "CPDLC got connected.");
167 _data_authority = message.sender;
168 _pDataAuthority->setStringValue(_data_authority); // make this known to ACFT
169 _status = CPDLC_ONLINE;
170 _pStatus->setIntValue(_status);
171 }
172 // do not process message if sender does not match our current data authority
173 if (message.sender != _data_authority) {
174 SG_LOG(SG_NETWORK, SG_WARN, "Received CPDLC message from foreign authority.");
175 return;
176 }
177 // connection rejected, or terminated by ATC
178 if (message.textline.find(CPDLC_MSGPREFIX_DISCONNECT) == 0) {
179 if (message.sender == _data_authority) {
180 SG_LOG(SG_NETWORK, SG_INFO, "CPDLC got disconnect.");
181 disconnect();
182 }
183 }
184 // store valid message in queue for later retrieval by aircraft
185 else if (message.textline.find(CPDLC_MSGPREFIX_MSG) == 0) {
186 SG_LOG(SG_NETWORK, SG_INFO, "CPDLC message");
187 _incoming_messages.push_back(message);
188 _pNewMessage->setBoolValue(1);
189 }
190}
bool connect(const std::string authority)
Definition cpdlc.cxx:69
~CPDLCManager()
Definition cpdlc.cxx:53
void getMessage()
Definition cpdlc.cxx:124
void update()
Definition cpdlc.cxx:141
bool send(const std::string message)
Definition cpdlc.cxx:115
CPDLCManager(IRCConnection *irc)
Definition cpdlc.cxx:38
void disconnect()
Definition cpdlc.cxx:104
const std::string CPDLC_MSGPREFIX_CONNECT
Definition cpdlc.cxx:30
const std::string CPDLC_MSGPREFIX_DISCONNECT
Definition cpdlc.cxx:32
const std::string CPDLC_MSGPREFIX_MSG
Definition cpdlc.cxx:31
const std::string CPDLC_IRC_SERVER
Definition cpdlc.cxx:29
@ CPDLC_OFFLINE
Definition cpdlc.hxx:39
@ CPDLC_CONNECTING
Definition cpdlc.hxx:40
@ CPDLC_ONLINE
Definition cpdlc.hxx:41
@ CPDLC_WAIT_IRC_READY
Definition cpdlc.hxx:44
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
std::string sender
Definition mpirc.hxx:41
std::string textline
Definition mpirc.hxx:42