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