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