OpenCPN Partial API docs
Loading...
Searching...
No Matches
comm_n0183_output.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: NMEA Data Multiplexer Object
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26// For compilers that support precompilation, includes "wx.h".
27#include <wx/wxprec.h>
28
29#ifndef WX_PRECOMP // precompiled headers
30#include <wx/wx.h>
31#endif
32
33#include "config.h"
34
35#include <wx/jsonreader.h>
36#include <wx/jsonval.h>
37#include <wx/jsonwriter.h>
38#include <wx/tokenzr.h>
39
40#include "model/comm_driver.h"
42#include "model/comm_drv_n0183_android_bt.h"
46#include "model/comm_n0183_output.h"
47#include "model/config_vars.h"
48#include "model/conn_params.h"
49#include "model/logger.h"
50#include "model/nmea_ctx_factory.h"
51#include "model/nmea_log.h"
52#include "model/route.h"
53#include "nmea0183.h"
54
55#ifdef USE_GARMINHOST
56#include "model/garmin_wrapper.h"
57#endif
58
59void LogBroadcastOutputMessageColor(const std::shared_ptr<const NavMsg>& msg,
60 NavmsgStatus ns, NmeaLog* nmea_log) {
61 if (nmea_log->IsVisible()) {
62 ns.direction = NavmsgStatus::Direction::kOutput;
63 Logline ll(msg, ns);
64 nmea_log->Add(ll);
65 }
66}
67
68void BroadcastNMEA0183Message(const wxString& msg, NmeaLog* nmea_log,
69 EventVar& on_msg_sent) {
70 auto& registry = CommDriverRegistry::GetInstance();
71 const std::vector<std::unique_ptr<AbstractCommDriver>>& drivers =
72 registry.GetDrivers();
73
74 for (auto& driver : drivers) {
75 if (driver->bus == NavAddr::Bus::N0183) {
76 auto drv_n0183 = dynamic_cast<CommDriverN0183*>(driver.get());
77 assert(drv_n0183);
78 ConnectionParams params = drv_n0183->GetParams();
79
80 if (params.IOSelect == DS_TYPE_INPUT_OUTPUT ||
81 params.IOSelect == DS_TYPE_OUTPUT) {
82 std::string id = msg.ToStdString().substr(1, 5);
83 auto source_addr =
84 std::make_shared<NavAddr>(NavAddr0183(params.GetStrippedDSPort()));
85 auto msg_out =
86 std::make_shared<Nmea0183Msg>(id, msg.ToStdString(), source_addr);
87 NavmsgStatus ns;
88 ns.direction = NavmsgStatus::Direction::kOutput;
89 if (params.SentencePassesFilter(msg, FILTER_OUTPUT)) {
90 bool xmit_ok = driver->SendMessage(msg_out, source_addr);
91 if (!xmit_ok) ns.status = NavmsgStatus::State::kTxError;
92 } else {
93 ns.accepted = NavmsgStatus::Accepted::kFilteredDropped;
94 }
95 LogBroadcastOutputMessageColor(msg_out, ns, nmea_log);
96 }
97 }
98 }
99 // Send to plugins
100 on_msg_sent.Notify(msg.ToStdString());
101}
102
103bool CreateOutputConnection(const wxString& com_name,
104 ConnectionParams& params_save, bool& btempStream,
105 bool& b_restoreStream, N0183DlgCtx dlg_ctx,
106 bool bGarminIn) {
107 AbstractCommDriver* driver(nullptr);
108 auto& registry = CommDriverRegistry::GetInstance();
109 const std::vector<DriverPtr>& drivers = registry.GetDrivers();
110
111 int baud = 0;
112 wxString comx;
113 bool bGarmin = false;
114 if (com_name.Lower().StartsWith("serial")) {
115 comx = com_name.AfterFirst(':'); // strip "Serial:"
116 comx =
117 comx.BeforeFirst(' '); // strip off any description provided by Windows
118 DriverPtr& old_driver = FindDriver(drivers, comx.ToStdString());
119 wxLogDebug("Looking for old stream %s", com_name);
120
121 if (old_driver) {
122 auto drv_serial_n0183 =
123 dynamic_cast<CommDriverN0183Serial*>(old_driver.get());
124 if (drv_serial_n0183) {
125 params_save = drv_serial_n0183->GetParams();
126 baud = params_save.Baudrate;
127 bGarmin = params_save.Garmin;
128 drv_serial_n0183->Close(); // Fast close
129 }
130 registry.Deactivate(old_driver);
131
132 b_restoreStream = true;
133 }
134
135 if (baud == 0) baud = 4800;
136 }
137 if (com_name.Lower().StartsWith("serial")) {
139 cp.Type = SERIAL;
140 cp.SetPortStr(comx);
141 cp.Baudrate = baud;
142 cp.Garmin = bGarminIn || bGarmin;
143 cp.IOSelect = DS_TYPE_OUTPUT;
144
145 MakeCommDriver(&cp);
146 btempStream = true;
147
148#ifdef __ANDROID__
149 wxMilliSleep(1000);
150#else
151 auto drv_serial_n0183 = dynamic_cast<CommDriverN0183Serial*>(driver);
152 if (drv_serial_n0183) {
153 if ((wxNOT_FOUND != com_name.Upper().Find("USB")) &&
154 (wxNOT_FOUND != com_name.Upper().Find("GARMIN"))) {
155 // Wait up to 1 seconds for serial Driver secondary thread to come up
156 int timeout = 0;
157 while (!drv_serial_n0183->IsGarminThreadActive() && (timeout < 50)) {
158 wxMilliSleep(100);
159 wxYield();
160 timeout++;
161 }
162
163 if (!drv_serial_n0183->IsGarminThreadActive()) {
164 MESSAGE_LOG << "-->GPS Port:" << com_name
165 << " ...Could not be opened for writing";
166 }
167 } else {
168 // Wait up to 1 seconds for serial Driver secondary thread to come up
169 int timeout = 0;
170 while (!drv_serial_n0183->IsSecThreadActive() && (timeout < 50)) {
171 wxMilliSleep(100);
172 timeout++;
173 }
174
175 if (!drv_serial_n0183->IsSecThreadActive()) {
176 MESSAGE_LOG << "-->GPS Port:" << com_name
177 << " ...Could not be opened for writing";
178 }
179 }
180 } else {
181 driver =
182 FindDriver(drivers, comx.ToStdString(), NavAddr::Bus::N0183).get();
183 }
184#endif
185 }
186
187 if (com_name.Find("Bluetooth") != wxNOT_FOUND) {
188 wxString comm_addr = com_name.AfterFirst(';');
189
190 driver = FindDriver(drivers, comm_addr.ToStdString()).get();
191 if (!driver) {
192 // Force Android Bluetooth to use only already enabled driver
193 return false;
194 }
195 } else if (com_name.Lower().StartsWith("udp") ||
196 com_name.Lower().StartsWith("tcp")) {
197 CommDriverN0183Net* drv_net_n0183(nullptr);
198 driver =
199 FindDriver(drivers, com_name.ToStdString(), NavAddr::Bus::N0183).get();
200
201 if (!driver) {
202 NetworkProtocol protocol = UDP;
203 if (com_name.Lower().StartsWith("tcp")) protocol = TCP;
204 wxStringTokenizer tkz(com_name, ":");
205 wxString token = tkz.GetNextToken();
206 wxString address = tkz.GetNextToken();
207 token = tkz.GetNextToken();
208 long port;
209 token.ToLong(&port);
210
212 cp.Type = NETWORK;
213 cp.NetProtocol = protocol;
214 cp.NetworkAddress = address;
215 cp.NetworkPort = port;
216 cp.IOSelect = DS_TYPE_OUTPUT; // DS_TYPE_INPUT_OUTPUT;
217
218 MakeCommDriver(&cp);
219 auto& me =
220 FindDriver(drivers, cp.GetStrippedDSPort(), cp.GetLastCommProtocol());
221 driver = me.get();
222 btempStream = true;
223 }
224 drv_net_n0183 = dynamic_cast<CommDriverN0183Net*>(driver);
225
226 if (com_name.Lower().StartsWith("tcp")) {
227 // new tcp connections must wait for connect
228 std::string msg(_("Connecting to "));
229 msg += com_name;
230 dlg_ctx.set_message(msg);
231 dlg_ctx.pulse();
232
233 if (drv_net_n0183) {
234 int loopCount = 10; // seconds
235 bool bconnected;
236 for (bconnected = false; !bconnected && (loopCount > 0); loopCount--) {
237 if (drv_net_n0183->GetSock()->IsConnected()) {
238 bconnected = true;
239 break;
240 }
241 dlg_ctx.pulse();
242 wxYield();
243 wxSleep(1);
244 }
245 if (bconnected) {
246 msg = _("Connected to ");
247 msg += com_name;
248 dlg_ctx.set_message(msg);
249 } else {
250 if (btempStream) {
251 auto& me = FindDriver(drivers, driver->iface, driver->bus);
252 registry.Deactivate(me);
253 }
254 return 0;
255 }
256 }
257 }
258 }
259 return driver != nullptr;
260}
261
262int PrepareOutputChannel(const wxString& com_name, N0183DlgCtx dlg_ctx,
263 ConnectionParams& params_save, bool& b_restoreStream,
264 bool& btempStream) {
265 int ret_val = 0;
266 auto& registry = CommDriverRegistry::GetInstance();
267
268 // Find any existing(i.e. open) serial com port with the same name,
269 // and query its parameters.
270 const std::vector<DriverPtr>& drivers = registry.GetDrivers();
271 bool is_garmin_serial = false;
272 CommDriverN0183Serial* drv_serial_n0183(nullptr);
273
274 if (com_name.Lower().StartsWith("serial")) {
275 wxString comx;
276 comx = com_name.AfterFirst(':'); // strip "Serial: + windows description
277 comx = comx.BeforeFirst(' ');
278 DriverPtr& existing_driver = FindDriver(drivers, comx.ToStdString());
279 wxLogDebug("Looking for old stream %s", com_name);
280
281 if (existing_driver) {
282 drv_serial_n0183 =
283 dynamic_cast<CommDriverN0183Serial*>(existing_driver.get());
284 if (drv_serial_n0183) {
285 is_garmin_serial = drv_serial_n0183->GetParams().Garmin;
286 }
287 }
288 }
289
290 // Special case for Garmin serial driver that is currently active
291 // We shall deactivate the current driver, and allow the self-contained
292 // Garmin stack to handle the object upload
293 // Also, save the driver's state, and mark for re-activation
294
295 if (is_garmin_serial) {
296 params_save = drv_serial_n0183->GetParams();
297 b_restoreStream = true;
298 drv_serial_n0183->Close(); // Fast close
299 auto& me =
300 FindDriver(drivers, drv_serial_n0183->GetParams().GetStrippedDSPort(),
301 drv_serial_n0183->GetParams().GetCommProtocol());
302 registry.Deactivate(me);
303 btempStream = true;
304 }
305 bool conn_ok =
306 CreateOutputConnection(com_name, params_save, btempStream,
307 b_restoreStream, dlg_ctx, is_garmin_serial);
308 if (!conn_ok) return 1;
309
310#ifdef xUSE_GARMINHOST
311#ifdef __WXMSW__
312 if (com_name.Upper().Matches("*GARMIN*")) // Garmin USB Mode
313 {
314 // if(m_pdevmon)
315 // m_pdevmon->StopIOThread(true);
316
317 auto drv_n0183_serial = dynamic_cast<CommDriverN0183Serial*>(driver.get());
318 drv_n0183_serial->StopGarminUSBIOThread(true);
319
320 if (!drv_n0183_serial->IsGarminThreadActive()) {
321 int v_init = Garmin_GPS_Init(wxString("usb:"));
322 if (v_init < 0) {
323 MESSAGE_LOG << "Garmin USB GPS could not be initialized, last error: "
324 << v_init << " LastGarminError: " << GetLastGarminError();
325
326 ret_val = ERR_GARMIN_INITIALIZE;
327 } else {
328 MESSAGE_LOG << "Garmin USB Initialized, unit identifies as: "
329 << Garmin_GPS_GetSaveString();
330 }
331 }
332 wxLogMessage("Sending Waypoint...");
333
334 // Create a RoutePointList with one item
335 RoutePointList rplist;
336 rplist.Append(prp);
337
338 int ret1 = Garmin_GPS_SendWaypoints(wxString("usb:"), &rplist);
339
340 if (ret1 != 1) {
341 MESSAGE_LOG << "Error Sending Waypoint to Garmin USB, last error: "
342 << GetLastGarminError();
343
344 ret_val = ERR_GARMIN_SEND_MESSAGE;
345 } else
346 ret_val = 0;
347
348 goto ret_point;
349 }
350
351#endif
352#endif
353 return ret_val;
354}
355int SendRouteToGPS_N0183(Route* pr, const wxString& com_name,
356 bool bsend_waypoints, Multiplexer& multiplexer,
357 N0183DlgCtx dlg_ctx) {
358 int ret_val = 0;
359
360 ConnectionParams params_save;
361 bool b_restoreStream = false;
362 bool btempStream = false;
363
364 auto& registry = CommDriverRegistry::GetInstance();
365
366 PrepareOutputChannel(com_name, dlg_ctx, params_save, b_restoreStream,
367 btempStream);
368
369 std::string target_iface =
370 com_name.AfterFirst(':').ToStdString(); // For serial ports
371 if (com_name.Lower().StartsWith("udp") || com_name.Lower().StartsWith("tcp"))
372 target_iface = com_name.ToStdString(); // for network
373
374 auto& target_driver =
375 FindDriver(registry.GetDrivers(), target_iface, NavAddr::Bus::N0183);
376
377 auto drv_n0183 = dynamic_cast<CommDriverN0183*>(target_driver.get());
378 if (!drv_n0183) {
379 return ERR_GPS_DRIVER_NOT_AVAILAIBLE;
380 }
381
382#ifdef USE_GARMINHOST
383#ifdef __WXMSW__
384 if (com_name.Upper().Matches("*GARMIN*")) // Garmin USB Mode
385 {
386 auto drv_serial_n0183 =
387 dynamic_cast<CommDriverN0183Serial*>(target_driver.get());
388 if (drv_serial_n0183) {
389 drv_serial_n0183->Close(); // Fast close
390 auto& me = FindDriver(CommDriverRegistry::GetInstance().GetDrivers(),
391 drv_serial_n0183->GetParams().GetStrippedDSPort(),
392 drv_serial_n0183->GetParams().GetCommProtocol());
393 registry.Deactivate(me);
394 }
395
396 {
397 int v_init = Garmin_GPS_Init(wxString("usb:"));
398 if (v_init < 0) {
399 MESSAGE_LOG << "Garmin USB GPS could not be initialized, error code: "
400 << v_init << " LastGarminError: " << GetLastGarminError();
401 ret_val = ERR_GARMIN_INITIALIZE;
402 } else {
403 MESSAGE_LOG << "Garmin USB initialized, unit identifies as "
404 << Garmin_GPS_GetSaveString();
405 }
406
407 wxLogMessage("Sending Routes...");
408 int ret1 = Garmin_GPS_SendRoute(wxString("usb:"), pr, dlg_ctx);
409
410 if (ret1 != 1) {
411 MESSAGE_LOG << " Error sending routes, last garmin error: "
412 << GetLastGarminError();
413 ret_val = ERR_GARMIN_SEND_MESSAGE;
414 } else
415 ret_val = 0;
416 }
417
418 goto ret_point_1;
419 }
420#endif
421#endif
422
423 if (g_bGarminHostUpload) {
424 // Close and abandon the tentatively opened target_driver
425 auto drv_serial_n0183 =
426 dynamic_cast<CommDriverN0183Serial*>(target_driver.get());
427 if (drv_serial_n0183) {
428 drv_serial_n0183->Close(); // Fast close
429 auto& me = FindDriver(CommDriverRegistry::GetInstance().GetDrivers(),
430 drv_serial_n0183->GetParams().GetStrippedDSPort(),
431 drv_serial_n0183->GetParams().GetCommProtocol());
432 registry.Deactivate(me);
433 }
434
435 int lret_val;
436 dlg_ctx.set_value(20);
437
438 wxString short_com = com_name.Mid(7);
439 // Initialize the Garmin receiver, build required Jeeps internal data
440 // structures
441 // Retry 5 times, 1 sec cycle
442 int n_try = 5;
443 int v_init = 0;
444 while (n_try) {
445 v_init = Garmin_GPS_Init(short_com);
446 if (v_init >= 0) break;
447 n_try--;
448 wxMilliSleep(1000);
449 }
450 if (v_init < 0) {
451 MESSAGE_LOG << "Garmin GPS could not be initialized on port: "
452 << short_com << " Error Code: " << v_init
453 << " LastGarminError: " << GetLastGarminError();
454
455 ret_val = ERR_GARMIN_INITIALIZE;
456 goto ret_point;
457 } else {
458 MESSAGE_LOG << "Sendig Route to Garmin GPS on port: " << short_com
459 << "Unit identifies as: " << Garmin_GPS_GetSaveString();
460 }
461
462 dlg_ctx.set_value(40);
463 lret_val = Garmin_GPS_SendRoute(short_com, pr, dlg_ctx);
464 if (lret_val != 1) {
465 MESSAGE_LOG << "Error Sending Route to Garmin GPS on port: " << short_com
466 << " Error Code: " << lret_val
467 << " LastGarminError: " << GetLastGarminError();
468 ret_val = ERR_GARMIN_SEND_MESSAGE;
469 goto ret_point;
470 } else {
471 ret_val = 0;
472 }
473
474 ret_point:
475
476 dlg_ctx.set_value(100);
477
478 wxMilliSleep(500);
479
480 goto ret_point_1;
481 } else
482
483 {
484 auto address =
485 std::make_shared<NavAddr>(NavAddr::Bus::N0183, drv_n0183->iface);
486 SENTENCE snt;
487 NMEA0183 oNMEA0183(NmeaCtxFactory());
488 oNMEA0183.TalkerID = _T ( "EC" );
489
490 int nProg = pr->pRoutePointList->GetCount() + 1;
491 dlg_ctx.set_range(100);
492
493 int progress_stall = 500;
494 if (pr->pRoutePointList->GetCount() > 10) progress_stall = 200;
495
496 // if (!dialog) progress_stall = 200; // 80 chars at 4800 baud is
497 // ~160 msec
498
499 // Send out the waypoints, in order
500 if (bsend_waypoints) {
501 wxRoutePointListNode* node = pr->pRoutePointList->GetFirst();
502
503 int ip = 1;
504 while (node) {
505 RoutePoint* prp = node->GetData();
506
507 if (g_GPS_Ident == "Generic") {
508 if (prp->m_lat < 0.)
509 oNMEA0183.Wpl.Position.Latitude.Set(-prp->m_lat, _T ( "S" ));
510 else
511 oNMEA0183.Wpl.Position.Latitude.Set(prp->m_lat, _T ( "N" ));
512
513 if (prp->m_lon < 0.)
514 oNMEA0183.Wpl.Position.Longitude.Set(-prp->m_lon, _T ( "W" ));
515 else
516 oNMEA0183.Wpl.Position.Longitude.Set(prp->m_lon, _T ( "E" ));
517
518 oNMEA0183.Wpl.To = prp->GetName().Truncate(g_maxWPNameLength);
519
520 oNMEA0183.Wpl.Write(snt);
521
522 } else if (g_GPS_Ident == "FurunoGP3X") {
523 // Furuno has its own talker ID, so do not allow the global
524 // override
525 wxString talker_save = g_TalkerIdText;
526 g_TalkerIdText.Clear();
527
528 oNMEA0183.TalkerID = _T ( "PFEC," );
529
530 if (prp->m_lat < 0.)
531 oNMEA0183.GPwpl.Position.Latitude.Set(-prp->m_lat, _T ( "S" ));
532 else
533 oNMEA0183.GPwpl.Position.Latitude.Set(prp->m_lat, _T ( "N" ));
534
535 if (prp->m_lon < 0.)
536 oNMEA0183.GPwpl.Position.Longitude.Set(-prp->m_lon, _T ( "W" ));
537 else
538 oNMEA0183.GPwpl.Position.Longitude.Set(prp->m_lon, _T ( "E" ));
539
540 wxString name = prp->GetName();
541 name += "000000";
542 name.Truncate(g_maxWPNameLength);
543 oNMEA0183.GPwpl.To = name;
544
545 oNMEA0183.GPwpl.Write(snt);
546
547 g_TalkerIdText = talker_save;
548 }
549
550 wxString payload = snt.Sentence;
551
552 // for some gps, like some garmin models, they assume the first
553 // waypoint in the route is the boat location, therefore it is
554 // dropped. These gps also can only accept a maximum of up to 20
555 // waypoints at a time before a delay is needed and a new string of
556 // waypoints may be sent. To ensure all waypoints will arrive, we can
557 // simply send each one twice. This ensures that the gps will get the
558 // waypoint and also allows us to send as many as we like
559 //
560 // We need only send once for FurunoGP3X models
561
562 auto msg_out = std::make_shared<Nmea0183Msg>(
563 "ECWPL", snt.Sentence.ToStdString(), address);
564
565 drv_n0183->SendMessage(msg_out, std::make_shared<NavAddr>());
566 if (g_GPS_Ident != "FurunoGP3X")
567 drv_n0183->SendMessage(msg_out, std::make_shared<NavAddr>());
568
569 NavmsgStatus ns;
570 ns.direction = NavmsgStatus::Direction::kOutput;
571 multiplexer.LogOutputMessage(msg_out, ns);
572 auto msg =
573 wxString("-->GPS Port: ") + com_name + " Sentence: " + snt.Sentence;
574 msg.Trim();
575 wxLogMessage(msg);
576
577 dlg_ctx.set_value((ip * 100) / nProg);
578
579 wxMilliSleep(progress_stall);
580
581 node = node->GetNext();
582 ip++;
583 }
584 }
585
586 // Create the NMEA Rte sentence
587 // Try to create a single sentence, and then check the length to see if
588 // too long
589 unsigned int max_length = 76;
590 unsigned int max_wp = 2; // seems to be required for garmin...
591
592 // Furuno GPS can only accept 5 (five) waypoint linkage sentences....
593 // So, we need to compact a few more points into each link sentence.
594 if (g_GPS_Ident == "FurunoGP3X") {
595 max_wp = 8;
596 max_length = 80;
597 }
598
599 // Furuno has its own talker ID, so do not allow the global override
600 wxString talker_save = g_TalkerIdText;
601 if (g_GPS_Ident == "FurunoGP3X") g_TalkerIdText.Clear();
602
603 oNMEA0183.Rte.Empty();
604 oNMEA0183.Rte.TypeOfRoute = CompleteRoute;
605
606 if (pr->m_RouteNameString.IsEmpty())
607 oNMEA0183.Rte.RouteName = _T ( "1" );
608 else
609 oNMEA0183.Rte.RouteName = pr->m_RouteNameString;
610
611 if (g_GPS_Ident == "FurunoGP3X") {
612 oNMEA0183.Rte.RouteName = _T ( "01" );
613 oNMEA0183.TalkerID = _T ( "GP" );
614 oNMEA0183.Rte.m_complete_char = 'C'; // override the default "c"
615 oNMEA0183.Rte.m_skip_checksum = 1; // no checksum needed
616 }
617
618 oNMEA0183.Rte.total_number_of_messages = 1;
619 oNMEA0183.Rte.message_number = 1;
620
621 // add the waypoints
622 auto node = pr->pRoutePointList->GetFirst();
623 while (node) {
624 RoutePoint* prp = node->GetData();
625 wxString name = prp->GetName().Truncate(g_maxWPNameLength);
626
627 if (g_GPS_Ident == "FurunoGP3X") {
628 name = prp->GetName();
629 name += "000000";
630 name.Truncate(g_maxWPNameLength);
631 name.Prepend(" "); // What Furuno calls "Skip Code", space means
632 // use the WP
633 }
634
635 oNMEA0183.Rte.AddWaypoint(name);
636 node = node->GetNext();
637 }
638
639 oNMEA0183.Rte.Write(snt);
640
641 if ((snt.Sentence.Len() > max_length) ||
642 (pr->pRoutePointList->GetCount() >
643 max_wp)) // Do we need split sentences?
644 {
645 // Make a route with zero waypoints to get tare load.
646 NMEA0183 tNMEA0183(NmeaCtxFactory());
647 SENTENCE tsnt;
648 tNMEA0183.TalkerID = _T ( "EC" );
649
650 tNMEA0183.Rte.Empty();
651 tNMEA0183.Rte.TypeOfRoute = CompleteRoute;
652
653 if (g_GPS_Ident != "FurunoGP3X") {
654 if (pr->m_RouteNameString.IsEmpty())
655 tNMEA0183.Rte.RouteName = _T ( "1" );
656 else
657 tNMEA0183.Rte.RouteName = pr->m_RouteNameString;
658
659 } else {
660 tNMEA0183.Rte.RouteName = _T ( "01" );
661 }
662
663 tNMEA0183.Rte.Write(tsnt);
664
665 unsigned int tare_length = tsnt.Sentence.Len();
666 tare_length -= 3; // Drop the checksum, for length calculations
667
668 wxArrayString sentence_array;
669
670 // Trial balloon: add the waypoints, with length checking
671 int n_total = 1;
672 bool bnew_sentence = true;
673 int sent_len = 0;
674 unsigned int wp_count = 0;
675
676 auto _node = pr->pRoutePointList->GetFirst();
677 while (_node) {
678 RoutePoint* prp = _node->GetData();
679 unsigned int name_len =
680 prp->GetName().Truncate(g_maxWPNameLength).Len();
681 if (g_GPS_Ident == "FurunoGP3X")
682 name_len = 7; // six chars, with leading space for "Skip Code"
683
684 if (bnew_sentence) {
685 sent_len = tare_length;
686 sent_len += name_len + 1; // with comma
687 bnew_sentence = false;
688 _node = _node->GetNext();
689 wp_count = 1;
690
691 } else {
692 if ((sent_len + name_len > max_length) || (wp_count >= max_wp)) {
693 n_total++;
694 bnew_sentence = true;
695 } else {
696 if (wp_count == max_wp)
697 sent_len += name_len; // with comma
698 else
699 sent_len += name_len + 1; // with comma
700 wp_count++;
701 _node = _node->GetNext();
702 }
703 }
704 }
705
706 // Now we have the sentence count, so make the real sentences using the
707 // same counting logic
708 int final_total = n_total;
709 int n_run = 1;
710 bnew_sentence = true;
711
712 _node = pr->pRoutePointList->GetFirst();
713 while (_node) {
714 RoutePoint* prp = _node->GetData();
715 wxString name = prp->GetName().Truncate(g_maxWPNameLength);
716 if (g_GPS_Ident == "FurunoGP3X") {
717 name = prp->GetName();
718 name += "000000";
719 name.Truncate(g_maxWPNameLength);
720 name.Prepend(" "); // What Furuno calls "Skip Code", space
721 // means use the WP
722 }
723
724 unsigned int name_len = name.Len();
725
726 if (bnew_sentence) {
727 sent_len = tare_length;
728 sent_len += name_len + 1; // comma
729 bnew_sentence = false;
730
731 oNMEA0183.Rte.Empty();
732 oNMEA0183.Rte.TypeOfRoute = CompleteRoute;
733
734 if (g_GPS_Ident != "FurunoGP3X") {
735 if (pr->m_RouteNameString.IsEmpty())
736 oNMEA0183.Rte.RouteName = "1";
737 else
738 oNMEA0183.Rte.RouteName = pr->m_RouteNameString;
739 } else {
740 oNMEA0183.Rte.RouteName = "01";
741 }
742
743 oNMEA0183.Rte.total_number_of_messages = final_total;
744 oNMEA0183.Rte.message_number = n_run;
745 snt.Sentence.Clear();
746 wp_count = 1;
747
748 oNMEA0183.Rte.AddWaypoint(name);
749 _node = _node->GetNext();
750 } else {
751 if ((sent_len + name_len > max_length) || (wp_count >= max_wp)) {
752 n_run++;
753 bnew_sentence = true;
754
755 oNMEA0183.Rte.Write(snt);
756
757 sentence_array.Add(snt.Sentence);
758 } else {
759 sent_len += name_len + 1; // comma
760 oNMEA0183.Rte.AddWaypoint(name);
761 wp_count++;
762 _node = _node->GetNext();
763 }
764 }
765 }
766
767 oNMEA0183.Rte.Write(snt); // last one...
768 if (snt.Sentence.Len() > tare_length) sentence_array.Add(snt.Sentence);
769
770 for (unsigned int ii = 0; ii < sentence_array.GetCount(); ii++) {
771 wxString sentence = sentence_array[ii];
772
773 auto msg_out = std::make_shared<Nmea0183Msg>(
774 "ECRTE", sentence.ToStdString(), std::make_shared<NavAddr>());
775 drv_n0183->SendMessage(msg_out, address);
776
777 NavmsgStatus ns;
778 ns.direction = NavmsgStatus::Direction::kOutput;
779 multiplexer.LogOutputMessage(msg_out, ns);
780 wxYield();
781
782 // LogOutputMessage(sentence, dstr->GetPort(), false);
783 auto msg =
784 wxString("-->GPS Port: ") + com_name + " Sentence: " + sentence;
785 msg.Trim();
786 wxLogMessage(msg);
787
788 wxMilliSleep(progress_stall);
789 }
790
791 } else {
792 auto msg_out = std::make_shared<Nmea0183Msg>(
793 "ECRTE", snt.Sentence.ToStdString(), address);
794 drv_n0183->SendMessage(msg_out, address);
795
796 NavmsgStatus ns;
797 ns.direction = NavmsgStatus::Direction::kOutput;
798 multiplexer.LogOutputMessage(msg_out, ns);
799 wxYield();
800
801 auto msg =
802 wxString("-->GPS Port:") + com_name + " Sentence: " + snt.Sentence;
803 msg.Trim();
804 wxLogMessage(msg);
805 }
806
807 if (g_GPS_Ident == "FurunoGP3X") {
808 wxString name = pr->GetName();
809 if (name.IsEmpty()) name = "RTECOMMENT";
810 wxString rte;
811 rte.Printf("$PFEC,GPrtc,01,");
812 rte += name.Left(16);
813 wxString rtep;
814 rtep.Printf(",%c%c", 0x0d, 0x0a);
815 rte += rtep;
816
817 auto msg_out =
818 std::make_shared<Nmea0183Msg>("GPRTC", rte.ToStdString(), address);
819 drv_n0183->SendMessage(msg_out, address);
820 NavmsgStatus ns;
821 ns.direction = NavmsgStatus::Direction::kOutput;
822 multiplexer.LogOutputMessage(msg_out, ns);
823
824 auto msg = wxString("-->GPS Port:") + com_name + " Sentence: " + rte;
825 msg.Trim();
826 wxLogMessage(msg);
827
828 wxString term;
829 term.Printf("$PFEC,GPxfr,CTL,E%c%c", 0x0d, 0x0a);
830
831 auto msg_outf =
832 std::make_shared<Nmea0183Msg>("GPRTC", term.ToStdString(), address);
833 drv_n0183->SendMessage(msg_outf, address);
834
835 ns = NavmsgStatus();
836 ns.direction = NavmsgStatus::Direction::kOutput;
837 multiplexer.LogOutputMessage(msg_outf, ns);
838
839 msg = wxString("-->GPS Port:") + com_name + " Sentence: " + term;
840 msg.Trim();
841 wxLogMessage(msg);
842 }
843 dlg_ctx.set_value(100);
844
845 wxMilliSleep(progress_stall);
846
847 ret_val = 0;
848
849 if (g_GPS_Ident == "FurunoGP3X") g_TalkerIdText = talker_save;
850 }
851ret_point_1:
852 // All finished with the temp port
853 if (btempStream) {
854 registry.Deactivate(target_driver);
855 }
856
857 if (b_restoreStream) {
858 wxMilliSleep(500); // Give temp driver a chance to die
859 MakeCommDriver(&params_save);
860 }
861
862 return ret_val;
863}
864
865int SendWaypointToGPS_N0183(RoutePoint* prp, const wxString& com_name,
866 Multiplexer& multiplexer, N0183DlgCtx dlg_ctx) {
867 int ret_val = 0;
868
869 ConnectionParams params_save;
870 bool b_restoreStream = false;
871 bool btempStream = false;
872
873 auto& registry = CommDriverRegistry::GetInstance();
874
875 PrepareOutputChannel(com_name, dlg_ctx, params_save, b_restoreStream,
876 btempStream);
877
878 auto& target_driver =
879 FindDriver(registry.GetDrivers(), com_name.AfterFirst(':').ToStdString(),
880 NavAddr::Bus::N0183);
881
882#ifdef USE_GARMINHOST
883#ifdef __WXMSW__
884 if (com_name.Upper().Matches("*GARMIN*")) // Garmin USB Mode
885 {
886 auto drv_serial_n0183 =
887 dynamic_cast<CommDriverN0183Serial*>(target_driver.get());
888 if (drv_serial_n0183) {
889 drv_serial_n0183->Close(); // Fast close
890 auto& me = FindDriver(CommDriverRegistry::GetInstance().GetDrivers(),
891 drv_serial_n0183->GetParams().GetStrippedDSPort(),
892 drv_serial_n0183->GetParams().GetCommProtocol());
893
894 registry.Deactivate(me);
895 }
896
897 {
898 int v_init = Garmin_GPS_Init(wxString("usb:"));
899 if (v_init < 0) {
900 MESSAGE_LOG << "Garmin USB GPS could not be initialized, last error: "
901 << v_init << " LastGarminError: " << GetLastGarminError();
902
903 ret_val = ERR_GARMIN_INITIALIZE;
904 } else {
905 MESSAGE_LOG << "Garmin USB Initialized, unit identifies as: "
906 << Garmin_GPS_GetSaveString();
907 }
908 }
909 wxLogMessage("Sending Waypoint...");
910
911 // Create a RoutePointList with one item
912 RoutePointList rplist;
913 rplist.Append(prp);
914
915 int ret1 = Garmin_GPS_SendWaypoints(wxString("usb:"), &rplist);
916
917 if (ret1 != 1) {
918 MESSAGE_LOG << "Error Sending Waypoint to Garmin USB, last error: "
919 << GetLastGarminError();
920
921 ret_val = ERR_GARMIN_SEND_MESSAGE;
922 } else
923 ret_val = 0;
924
925 goto ret_point;
926 }
927
928#endif
929#endif
930
931#ifdef USE_GARMINHOST
932 // Are we using Garmin Host mode for uploads?
933 if (g_bGarminHostUpload) {
934 // Close and abandon the tentatively opened target_driver
935 auto serial_n0183 =
936 dynamic_cast<CommDriverN0183Serial*>(target_driver.get());
937 if (serial_n0183) {
938 serial_n0183->Close(); // Fast close
939 auto& me = FindDriver(CommDriverRegistry::GetInstance().GetDrivers(),
940 serial_n0183->GetParams().GetStrippedDSPort(),
941 serial_n0183->GetParams().GetCommProtocol());
942 registry.Deactivate(me);
943 }
944 RoutePointList rplist;
945
946 wxString short_com = com_name.Mid(7);
947 // Initialize the Garmin receiver, build required Jeeps internal data
948 // structures
949 // Retry 5 times, 1 sec cycle
950 int n_try = 5;
951 int v_init = 0;
952 while (n_try) {
953 v_init = Garmin_GPS_Init(short_com);
954 if (v_init >= 0) break;
955 n_try--;
956 wxMilliSleep(1000);
957 }
958 if (v_init < 0) {
959 MESSAGE_LOG << "Garmin GPS could not be initialized on port: " << com_name
960 << " Error Code: " << v_init
961 << "LastGarminError: " << GetLastGarminError();
962
963 ret_val = ERR_GARMIN_INITIALIZE;
964 goto ret_point;
965 } else {
966 MESSAGE_LOG << "Sending waypoint(s) to Garmin GPS on port: " << com_name;
967 MESSAGE_LOG << "Unit identifies as: " << Garmin_GPS_GetSaveString();
968 }
969
970 // Create a RoutePointList with one item
971 rplist.Append(prp);
972
973 ret_val = Garmin_GPS_SendWaypoints(short_com, &rplist);
974 if (ret_val != 1) {
975 MESSAGE_LOG << "Error Sending Waypoint(s) to Garmin GPS on port, "
976 << com_name << " error code: " << ret_val
977 << ", last garmin error: " << GetLastGarminError();
978 ret_val = ERR_GARMIN_SEND_MESSAGE;
979 goto ret_point;
980 } else
981 ret_val = 0;
982
983 goto ret_point;
984 } else
985#endif // USE_GARMINHOST
986
987 { // Standard NMEA mode
988 auto drv_n0183 = dynamic_cast<CommDriverN0183*>(target_driver.get());
989 if (!drv_n0183) {
990 ret_val = ERR_GPS_DRIVER_NOT_AVAILAIBLE;
991 goto ret_point;
992 }
993
994 auto address = std::make_shared<NavAddr>();
995 SENTENCE snt;
996 NMEA0183 oNMEA0183(NmeaCtxFactory());
997 oNMEA0183.TalkerID = "EC";
998 dlg_ctx.set_range(100);
999
1000 if (g_GPS_Ident == "Generic") {
1001 if (prp->m_lat < 0.)
1002 oNMEA0183.Wpl.Position.Latitude.Set(-prp->m_lat, "S");
1003 else
1004 oNMEA0183.Wpl.Position.Latitude.Set(prp->m_lat, "N");
1005
1006 if (prp->m_lon < 0.)
1007 oNMEA0183.Wpl.Position.Longitude.Set(-prp->m_lon, "W");
1008 else
1009 oNMEA0183.Wpl.Position.Longitude.Set(prp->m_lon, "E");
1010
1011 oNMEA0183.Wpl.To = prp->GetName().Truncate(g_maxWPNameLength);
1012
1013 oNMEA0183.Wpl.Write(snt);
1014 } else if (g_GPS_Ident == "FurunoGP3X") {
1015 oNMEA0183.TalkerID = "PFEC,";
1016
1017 if (prp->m_lat < 0.)
1018 oNMEA0183.GPwpl.Position.Latitude.Set(-prp->m_lat, "S");
1019 else
1020 oNMEA0183.GPwpl.Position.Latitude.Set(prp->m_lat, "N");
1021
1022 if (prp->m_lon < 0.)
1023 oNMEA0183.GPwpl.Position.Longitude.Set(-prp->m_lon, "W");
1024 else
1025 oNMEA0183.GPwpl.Position.Longitude.Set(prp->m_lon, "E");
1026
1027 wxString name = prp->GetName();
1028 name += "000000";
1029 name.Truncate(g_maxWPNameLength);
1030 oNMEA0183.GPwpl.To = name;
1031
1032 oNMEA0183.GPwpl.Write(snt);
1033 }
1034
1035 auto msg_out = std::make_shared<Nmea0183Msg>(
1036 "ECWPL", snt.Sentence.ToStdString(), address);
1037 drv_n0183->SendMessage(msg_out, address);
1038
1039 NavmsgStatus ns;
1040 ns.direction = NavmsgStatus::Direction::kOutput;
1041 multiplexer.LogOutputMessage(msg_out, ns);
1042 auto msg = wxString("-->GPS Port:") + com_name + " Sentence: ";
1043 msg.Trim();
1044 wxLogMessage(msg);
1045
1046 if (g_GPS_Ident == "FurunoGP3X") {
1047 wxString term;
1048 term.Printf("$PFEC,GPxfr,CTL,E%c%c", 0x0d, 0x0a);
1049
1050 // driver->SendSentence(term);
1051 // LogOutputMessage(term, dstr->GetPort(), false);
1052
1053 auto logmsg = wxString("-->GPS Port:") + com_name + " Sentence: " + term;
1054 logmsg.Trim();
1055 wxLogMessage(logmsg);
1056 }
1057 dlg_ctx.set_value(100);
1058
1059 wxMilliSleep(500);
1060
1061 ret_val = 0;
1062 }
1063
1064ret_point:
1065 // All finished with the temp port
1066 if (btempStream) registry.Deactivate(target_driver);
1067
1068 if (b_restoreStream) {
1069 MakeCommDriver(&params_save);
1070 }
1071 return ret_val;
1072}
Common interface for all drivers.
Definition comm_driver.h:67
NMEA0183 basic parsing common parts:
Generic event handling between MVC Model and Controller based on a shared EventVar variable.
void Notify() override
Notify all listeners, no data supplied.
Handle logging and forwarding of incoming n0183/n2k messages.
Definition multiplexer.h:58
Representation of message status as determined by the multiplexer.
NMEA Log Interface.
Definition nmea_log.h:72
virtual bool IsVisible() const =0
Return true if log is visible i.e., if it's any point using Add().
virtual void Add(const Logline &l)=0
Add a formatted string to log output.
Represents a waypoint or mark within the navigation system.
Definition route_point.h:70
Represents a navigational route in the navigation system.
Definition route.h:98
RoutePointList * pRoutePointList
Ordered list of waypoints (RoutePoints) that make up this route.
Definition route.h:335
wxString m_RouteNameString
User-assigned name for the route.
Definition route.h:246
Communication driver layer.
void MakeCommDriver(const ConnectionParams *params)
Create and register a driver for given connection.
Communication drivers factory and support.
NMEA0183 over IP driver.
NMEA0183 serial driver.
DriverPtr & FindDriver(const std::vector< DriverPtr > &drivers, const std::string &iface, const NavAddr::Bus _bus)
Search list of drivers for a driver with given interface string.
Driver registration container, a singleton.
Enhanced logging interface on top of wx/log.h.
Basic DataMonitor logging interface: LogLine (reflects a line in the log) and NmeaLog,...
Item in the log window.
Definition nmea_log.h:34