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