Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5bcd8f4

Browse files
KerryMoffittRtnKerry Moffitt
authored and
Kerry Moffitt
committedSep 10, 2018
Allow for AdHocConnections in MavLinkCom
1 parent 64bc4b3 commit 5bcd8f4

6 files changed

+521
-0
lines changed
 

‎MavLinkCom/MavLinkCom.vcxproj

+4
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@
345345
<ItemGroup>
346346
<ClCompile Include="common_utils\FileSystem.cpp" />
347347
<ClCompile Include="common_utils\ThreadUtils.cpp" />
348+
<ClCompile Include="src\AdHocConnection.cpp" />
349+
<ClCompile Include="src\impl\AdHocConnectionImpl.cpp" />
348350
<ClCompile Include="src\impl\MavLinkFtpClientImpl.cpp" />
349351
<ClCompile Include="src\impl\MavLinkNodeImpl.cpp" />
350352
<ClCompile Include="src\impl\MavLinkTcpServerImpl.cpp" />
@@ -374,6 +376,7 @@
374376
<ClInclude Include="common_utils\json.hpp" />
375377
<ClInclude Include="common_utils\StrictMode.hpp" />
376378
<ClInclude Include="common_utils\ThreadUtils.hpp" />
379+
<ClInclude Include="include\AdHocConnection.hpp" />
377380
<ClInclude Include="include\AsyncResult.hpp" />
378381
<ClInclude Include="common_utils\EnumFlags.hpp" />
379382
<ClInclude Include="common_utils\ExceptionUtils.hpp" />
@@ -384,6 +387,7 @@
384387
<ClInclude Include="common_utils\type_utils.hpp" />
385388
<ClInclude Include="common_utils\Utils.hpp" />
386389
<ClInclude Include="include\Semaphore.hpp" />
390+
<ClInclude Include="src\impl\AdHocConnectionImpl.hpp" />
387391
<ClInclude Include="src\impl\MavLinkFtpClientImpl.hpp" />
388392
<ClInclude Include="src\impl\MavLinkNodeImpl.hpp" />
389393
<ClInclude Include="src\impl\MavLinkTcpServerImpl.hpp" />

‎MavLinkCom/MavLinkCom.vcxproj.filters

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
<ClCompile Include="src\impl\windows\WindowsFindSerialPorts.cpp">
7777
<Filter>src\impl\windows</Filter>
7878
</ClCompile>
79+
<ClCompile Include="src\impl\AdHocConnectionImpl.cpp" />
80+
<ClCompile Include="src\AdHocConnection.cpp" />
7981
</ItemGroup>
8082
<ItemGroup>
8183
<ClInclude Include="mavlink\checksum.h">
@@ -213,6 +215,8 @@
213215
<ClInclude Include="src\impl\MavLinkFtpClientImpl.hpp">
214216
<Filter>src\impl</Filter>
215217
</ClInclude>
218+
<ClInclude Include="include\AdHocConnection.hpp" />
219+
<ClInclude Include="src\impl\AdHocConnectionImpl.hpp" />
216220
</ItemGroup>
217221
<ItemGroup>
218222
<Filter Include="Mavlink">
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#ifndef MavLinkCom_AdHocConnection_hpp
2+
#define MavLinkCom_AdHocConnection_hpp
3+
4+
#include <functional>
5+
#include <memory>
6+
#include <string>
7+
#include <vector>
8+
9+
#ifndef ONECORE
10+
#if defined(_WIN32) && defined(_MSC_VER )
11+
#pragma comment( lib, "Setupapi.lib" )
12+
#pragma comment( lib, "Cfgmgr32.lib" )
13+
#endif
14+
#endif
15+
16+
class Port;
17+
18+
namespace mavlinkcom_impl {
19+
class AdHocConnectionImpl;
20+
}
21+
22+
namespace mavlinkcom {
23+
24+
class AdHocConnection;
25+
26+
// This callback is invoked when a MavLink message is read from the connection.
27+
typedef std::function<void(std::shared_ptr<AdHocConnection> connection, const std::vector<uint8_t>& msg)> AdHocMessageHandler;
28+
29+
// This class represents a single connection to a remote non-mavlink node connected either over UDP, TCP or Serial port.
30+
// You can use this connection to send a message directly to that node, and start listening to messages
31+
// from that remote node. You can handle those messages directly using subscribe.
32+
class AdHocConnection : public std::enable_shared_from_this<AdHocConnection>
33+
{
34+
public:
35+
AdHocConnection();
36+
37+
// Start listening on a specific local port for packets from any remote computer. Once a packet is received
38+
// it will remember the remote address of the sender so that subsequend sendMessage calls will go back to that sender.
39+
// This is useful if the remote sender already knows which local port you plan to listen on.
40+
// The localAddr can also a specific local ip address if you need to specify which
41+
// network interface to use, for example, a corporate wired ethernet usually does not transmit UDP packets
42+
// to a wifi connected device, so in that case the localAddress needs to be the IP address of a specific wifi internet
43+
// adapter rather than 127.0.0.1.
44+
static std::shared_ptr<AdHocConnection> connectLocalUdp(const std::string& nodeName, std::string localAddr, int localPort);
45+
46+
// Connect to a specific remote machine that is already listening on a specific port for messages from any computer.
47+
// This will use any free local port that is available.
48+
// The localAddr can also a specific local ip address if you need to specify which
49+
// network interface to use, for example, a corporate wired ethernet usually does not transmit UDP packets
50+
// to a wifi connected device, so in that case the localAddress needs to be the IP address of a specific wifi internet
51+
// adapter rather than 127.0.0.1.
52+
static std::shared_ptr<AdHocConnection> connectRemoteUdp(const std::string& nodeName, std::string localAddr, std::string remoteAddr, int remotePort);
53+
54+
// instance methods
55+
bool isOpen();
56+
void close();
57+
58+
// provide a callback function that will be called for every message "received" from the remote mavlink node.
59+
int subscribe(AdHocMessageHandler handler);
60+
void unsubscribe(int id);
61+
62+
uint8_t getNextSequence();
63+
64+
// Pack and send the given message, assuming the compid and sysid have been set by the caller.
65+
void sendMessage(const std::vector<uint8_t> &msg);
66+
67+
protected:
68+
void startListening(const std::string& nodeName, std::shared_ptr<Port> connectedPort);
69+
70+
public:
71+
//needed for piml pattern
72+
~AdHocConnection();
73+
74+
private:
75+
std::unique_ptr<mavlinkcom_impl::AdHocConnectionImpl> pImpl;
76+
friend class MavLinkNode;
77+
friend class mavlinkcom_impl::AdHocConnectionImpl;
78+
};
79+
}
80+
81+
#endif

‎MavLinkCom/src/AdHocConnection.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "AdHocConnection.hpp"
2+
#include "impl/AdHocConnectionImpl.hpp"
3+
4+
using namespace mavlinkcom;
5+
using namespace mavlinkcom_impl;
6+
7+
AdHocConnection::AdHocConnection()
8+
{
9+
pImpl.reset(new AdHocConnectionImpl());
10+
}
11+
12+
std::shared_ptr<AdHocConnection> AdHocConnection::connectLocalUdp(const std::string& nodeName, std::string localAddr, int localPort)
13+
{
14+
return AdHocConnectionImpl::connectLocalUdp(nodeName, localAddr, localPort);
15+
}
16+
17+
std::shared_ptr<AdHocConnection> AdHocConnection::connectRemoteUdp(const std::string& nodeName, std::string localAddr, std::string remoteAddr, int remotePort)
18+
{
19+
return AdHocConnectionImpl::connectRemoteUdp(nodeName, localAddr, remoteAddr, remotePort);
20+
}
21+
22+
void AdHocConnection::startListening(const std::string& nodeName, std::shared_ptr<Port> connectedPort)
23+
{
24+
pImpl->startListening(shared_from_this(), nodeName, connectedPort);
25+
}
26+
27+
void AdHocConnection::close()
28+
{
29+
pImpl->close();
30+
}
31+
32+
bool AdHocConnection::isOpen()
33+
{
34+
return pImpl->isOpen();
35+
}
36+
37+
void AdHocConnection::sendMessage(const std::vector<uint8_t> &msg)
38+
{
39+
pImpl->sendMessage(msg);
40+
}
41+
42+
int AdHocConnection::subscribe(AdHocMessageHandler handler)
43+
{
44+
return pImpl->subscribe(handler);
45+
}
46+
47+
void AdHocConnection::unsubscribe(int id)
48+
{
49+
pImpl->unsubscribe(id);
50+
}
51+
52+
AdHocConnection::~AdHocConnection() {
53+
pImpl->close();
54+
pImpl = nullptr;
55+
}
+288
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#include "AdHocConnectionImpl.hpp"
5+
#include "Utils.hpp"
6+
#include "ThreadUtils.hpp"
7+
#include "../serial_com/Port.h"
8+
#include "../serial_com/SerialPort.hpp"
9+
#include "../serial_com/UdpClientPort.hpp"
10+
#include "../serial_com/TcpClientPort.hpp"
11+
12+
using namespace mavlink_utils;
13+
using namespace mavlinkcom_impl;
14+
15+
AdHocConnectionImpl::AdHocConnectionImpl()
16+
{
17+
closed = true;
18+
::memset(&mavlink_intermediate_status_, 0, sizeof(mavlink_status_t));
19+
::memset(&mavlink_status_, 0, sizeof(mavlink_status_t));
20+
}
21+
std::string AdHocConnectionImpl::getName() {
22+
return name;
23+
}
24+
25+
AdHocConnectionImpl::~AdHocConnectionImpl()
26+
{
27+
con_.reset();
28+
close();
29+
}
30+
31+
std::shared_ptr<AdHocConnection> AdHocConnectionImpl::createConnection(const std::string& nodeName, std::shared_ptr<Port> port)
32+
{
33+
// std::shared_ptr<MavLinkCom> owner, const std::string& nodeName
34+
std::shared_ptr<AdHocConnection> con = std::make_shared<AdHocConnection>();
35+
con->startListening(nodeName, port);
36+
return con;
37+
}
38+
39+
std::shared_ptr<AdHocConnection> AdHocConnectionImpl::connectLocalUdp(const std::string& nodeName, std::string localAddr, int localPort)
40+
{
41+
std::shared_ptr<UdpClientPort> socket = std::make_shared<UdpClientPort>();
42+
43+
socket->connect(localAddr, localPort, "", 0);
44+
45+
return createConnection(nodeName, socket);
46+
}
47+
48+
std::shared_ptr<AdHocConnection> AdHocConnectionImpl::connectRemoteUdp(const std::string& nodeName, std::string localAddr, std::string remoteAddr, int remotePort)
49+
{
50+
std::string local = localAddr;
51+
// just a little sanity check on the local address, if remoteAddr is localhost then localAddr must be also.
52+
if (remoteAddr == "127.0.0.1") {
53+
local = "127.0.0.1";
54+
}
55+
56+
std::shared_ptr<UdpClientPort> socket = std::make_shared<UdpClientPort>();
57+
58+
socket->connect(local, 0, remoteAddr, remotePort);
59+
60+
return createConnection(nodeName, socket);
61+
}
62+
63+
std::shared_ptr<AdHocConnection> AdHocConnectionImpl::connectTcp(const std::string& nodeName, std::string localAddr, const std::string& remoteIpAddr, int remotePort)
64+
{
65+
std::string local = localAddr;
66+
// just a little sanity check on the local address, if remoteAddr is localhost then localAddr must be also.
67+
if (remoteIpAddr == "127.0.0.1") {
68+
local = "127.0.0.1";
69+
}
70+
71+
std::shared_ptr<TcpClientPort> socket = std::make_shared<TcpClientPort>();
72+
73+
socket->connect(local, 0, remoteIpAddr, remotePort);
74+
75+
return createConnection(nodeName, socket);
76+
}
77+
78+
std::shared_ptr<AdHocConnection> AdHocConnectionImpl::connectSerial(const std::string& nodeName, std::string name, int baudRate, const std::string initString)
79+
{
80+
std::shared_ptr<SerialPort> serial = std::make_shared<SerialPort>();
81+
82+
int hr = serial->connect(name.c_str(), baudRate);
83+
if (hr != 0)
84+
throw std::runtime_error(Utils::stringf("Could not open the serial port %s, error=%d", name.c_str(), hr));
85+
86+
// send this right away just in case serial link is not already configured
87+
if (initString.size() > 0) {
88+
serial->write(reinterpret_cast<const uint8_t*>(initString.c_str()), static_cast<int>(initString.size()));
89+
}
90+
91+
return createConnection(nodeName, serial);
92+
}
93+
94+
void AdHocConnectionImpl::startListening(std::shared_ptr<AdHocConnection> parent, const std::string& nodeName, std::shared_ptr<Port> connectedPort)
95+
{
96+
name = nodeName;
97+
con_ = parent;
98+
close();
99+
closed = false;
100+
port = connectedPort;
101+
102+
Utils::cleanupThread(read_thread);
103+
read_thread = std::thread{ &AdHocConnectionImpl::readPackets, this };
104+
Utils::cleanupThread(publish_thread_);
105+
publish_thread_ = std::thread{ &AdHocConnectionImpl::publishPackets, this };
106+
}
107+
108+
void AdHocConnectionImpl::close()
109+
{
110+
closed = true;
111+
if (port != nullptr) {
112+
port->close();
113+
port = nullptr;
114+
}
115+
116+
if (read_thread.joinable()) {
117+
read_thread.join();
118+
}
119+
if (publish_thread_.joinable()) {
120+
msg_available_.post();
121+
publish_thread_.join();
122+
}
123+
}
124+
125+
bool AdHocConnectionImpl::isOpen()
126+
{
127+
return !closed;
128+
}
129+
130+
int AdHocConnectionImpl::getTargetComponentId()
131+
{
132+
return this->other_component_id;
133+
}
134+
int AdHocConnectionImpl::getTargetSystemId()
135+
{
136+
return this->other_system_id;
137+
}
138+
139+
void AdHocConnectionImpl::sendMessage(const std::vector<uint8_t>& msg)
140+
{
141+
if (closed) {
142+
return;
143+
}
144+
145+
try {
146+
port->write(msg.data(), static_cast<int>(msg.size()));
147+
}
148+
catch (std::exception& e) {
149+
throw std::runtime_error(Utils::stringf("AdHocConnectionImpl: Error sending message on connection '%s', details: %s", name.c_str(), e.what()));
150+
}
151+
}
152+
153+
154+
int AdHocConnectionImpl::subscribe(AdHocMessageHandler handler)
155+
{
156+
MessageHandlerEntry entry = { static_cast<int>(listeners.size() + 1), handler = handler };
157+
std::lock_guard<std::mutex> guard(listener_mutex);
158+
listeners.push_back(entry);
159+
snapshot_stale = true;
160+
return entry.id;
161+
}
162+
void AdHocConnectionImpl::unsubscribe(int id)
163+
{
164+
std::lock_guard<std::mutex> guard(listener_mutex);
165+
for (auto ptr = listeners.begin(), end = listeners.end(); ptr != end; ptr++)
166+
{
167+
if ((*ptr).id == id)
168+
{
169+
listeners.erase(ptr);
170+
snapshot_stale = true;
171+
break;
172+
}
173+
}
174+
}
175+
176+
void AdHocConnectionImpl::readPackets()
177+
{
178+
//CurrentThread::setMaximumPriority();
179+
std::shared_ptr<Port> safePort = this->port;
180+
const int MAXBUFFER = 512;
181+
uint8_t* buffer = new uint8_t[MAXBUFFER];
182+
int channel = 0;
183+
int hr = 0;
184+
while (hr == 0 && con_ != nullptr && !closed)
185+
{
186+
int read = 0;
187+
if (safePort->isClosed())
188+
{
189+
// hmmm, wait till it is opened?
190+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
191+
continue;
192+
}
193+
194+
int count = safePort->read(buffer, MAXBUFFER);
195+
if (count <= 0) {
196+
// error? well let's try again, but we should be careful not to spin too fast and kill the CPU
197+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
198+
continue;
199+
}
200+
201+
if (count >= MAXBUFFER) {
202+
203+
std::cerr << "GAH KM911 message size (" << std::to_string(count) << ") is bigger than max buffer size! Time to support frame breaks, Moffitt" << std::endl;
204+
205+
// error? well let's try again, but we should be careful not to spin too fast and kill the CPU
206+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
207+
continue;
208+
}
209+
210+
// queue event for publishing.
211+
{
212+
std::lock_guard<std::mutex> guard(msg_queue_mutex_);
213+
std::vector<uint8_t> message(count);
214+
memcpy(message.data(), buffer, count);
215+
msg_queue_.push(message);
216+
}
217+
218+
if (waiting_for_msg_) {
219+
msg_available_.post();
220+
}
221+
222+
} //while
223+
224+
delete[] buffer;
225+
226+
} //readPackets
227+
228+
void AdHocConnectionImpl::drainQueue()
229+
{
230+
std::vector<uint8_t> message;
231+
bool hasMsg = true;
232+
while (hasMsg) {
233+
hasMsg = false;
234+
{
235+
std::lock_guard<std::mutex> guard(msg_queue_mutex_);
236+
if (!msg_queue_.empty()) {
237+
message = msg_queue_.front();
238+
msg_queue_.pop();
239+
hasMsg = true;
240+
}
241+
}
242+
if (!hasMsg)
243+
{
244+
return;
245+
}
246+
// publish the message from this thread, this is safer than publishing from the readPackets thread
247+
// as it ensures we don't lose messages if the listener is slow.
248+
if (snapshot_stale) {
249+
// this is tricky, the clear has to be done outside the lock because it is destructing the handlers
250+
// and the handler might try and call unsubscribe, which needs to be able to grab the lock, otherwise
251+
// we would get a deadlock.
252+
snapshot.clear();
253+
254+
std::lock_guard<std::mutex> guard(listener_mutex);
255+
snapshot = listeners;
256+
snapshot_stale = false;
257+
}
258+
auto end = snapshot.end();
259+
260+
auto startTime = std::chrono::system_clock::now();
261+
std::shared_ptr<AdHocConnection> sharedPtr = std::shared_ptr<AdHocConnection>(this->con_);
262+
for (auto ptr = snapshot.begin(); ptr != end; ptr++)
263+
{
264+
try {
265+
(*ptr).handler(sharedPtr, message);
266+
}
267+
catch (std::exception& e) {
268+
Utils::log(Utils::stringf("AdHocConnectionImpl: Error handling message on connection '%s', details: %s",
269+
name.c_str(), e.what()), Utils::kLogLevelError);
270+
}
271+
}
272+
}
273+
}
274+
275+
void AdHocConnectionImpl::publishPackets()
276+
{
277+
//CurrentThread::setMaximumPriority();
278+
while (!closed) {
279+
280+
drainQueue();
281+
282+
waiting_for_msg_ = true;
283+
msg_available_.wait();
284+
waiting_for_msg_ = false;
285+
}
286+
}
287+
288+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#ifndef MavLinkCom_AdHocConnectionImpl_hpp
5+
#define MavLinkCom_AdHocConnectionImpl_hpp
6+
7+
#include <memory>
8+
#include <vector>
9+
#include <queue>
10+
#include <thread>
11+
#include <mutex>
12+
#include <unordered_set>
13+
#include "AdHocConnection.hpp"
14+
//#include "MavLinkMessageBase.hpp"
15+
#include "Semaphore.hpp"
16+
#include "../serial_com/TcpClientPort.hpp"
17+
#include "StrictMode.hpp"
18+
#define MAVLINK_PACKED
19+
20+
STRICT_MODE_OFF
21+
#include "../mavlink/common/mavlink.h"
22+
#include "../mavlink/mavlink_helpers.h"
23+
#include "../mavlink/mavlink_types.h"
24+
STRICT_MODE_ON
25+
26+
using namespace mavlinkcom;
27+
28+
namespace mavlinkcom_impl {
29+
30+
// See MavLinkConnection.hpp for definitions of these methods.
31+
class AdHocConnectionImpl
32+
{
33+
public:
34+
AdHocConnectionImpl();
35+
static std::shared_ptr<AdHocConnection> connectSerial(const std::string& nodeName, std::string portName, int baudrate = 115200, const std::string initString = "");
36+
static std::shared_ptr<AdHocConnection> connectLocalUdp(const std::string& nodeName, std::string localAddr, int localPort);
37+
static std::shared_ptr<AdHocConnection> connectRemoteUdp(const std::string& nodeName, std::string localAddr, std::string remoteAddr, int remotePort);
38+
static std::shared_ptr<AdHocConnection> connectTcp(const std::string& nodeName, std::string localAddr, const std::string& remoteIpAddr, int remotePort);
39+
40+
std::string getName();
41+
int getTargetComponentId();
42+
int getTargetSystemId();
43+
~AdHocConnectionImpl();
44+
void startListening(std::shared_ptr<AdHocConnection> parent, const std::string& nodeName, std::shared_ptr<Port> connectedPort);
45+
void close();
46+
bool isOpen();
47+
void sendMessage(const std::vector<uint8_t>& msg);
48+
int subscribe(AdHocMessageHandler handler);
49+
void unsubscribe(int id);
50+
51+
private:
52+
static std::shared_ptr<AdHocConnection> createConnection(const std::string& nodeName, std::shared_ptr<Port> port);
53+
void publishPackets();
54+
void readPackets();
55+
void drainQueue();
56+
std::string name;
57+
std::shared_ptr<Port> port;
58+
std::shared_ptr<AdHocConnection> con_;
59+
int other_system_id = -1;
60+
int other_component_id = 0;
61+
std::thread read_thread;
62+
std::string accept_node_name_;
63+
std::shared_ptr<TcpClientPort> server_;
64+
65+
struct MessageHandlerEntry {
66+
public:
67+
int id;
68+
AdHocMessageHandler handler;
69+
};
70+
std::vector<MessageHandlerEntry> listeners;
71+
std::vector<MessageHandlerEntry> snapshot;
72+
bool snapshot_stale;
73+
std::mutex listener_mutex;
74+
bool closed;
75+
std::thread publish_thread_;
76+
std::queue<std::vector<uint8_t>> msg_queue_;
77+
std::mutex msg_queue_mutex_;
78+
mavlink_utils::Semaphore msg_available_;
79+
bool waiting_for_msg_ = false;
80+
bool supports_mavlink2_ = false;
81+
bool signing_ = false;
82+
mavlink_status_t mavlink_intermediate_status_;
83+
mavlink_status_t mavlink_status_;
84+
std::mutex telemetry_mutex_;
85+
std::unordered_set<uint8_t> ignored_messageids;
86+
};
87+
}
88+
89+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.