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