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