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