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