OpenCPN Partial API docs
Loading...
Searching...
No Matches
std_serial_io.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2022 by David Register *
3 * Copyright (C) 2022-2024 Alec Leamas *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 **************************************************************************/
20
27#include <memory>
28#include <string>
29
30// For compilers that support precompilation, includes "wx.h".
31#include <wx/wxprec.h>
32
33#ifndef WX_PRECOMP
34#include <wx/wx.h>
35#endif
36
37#include <mutex> // std::mutex
38#include <queue> // std::queue
39#include <thread>
40#include <vector>
41
42#include <wx/log.h>
43#include <wx/string.h>
44#include <wx/utils.h>
45
46#include "model/comm_buffers.h"
48#include "model/logger.h"
49#include "model/serial_io.h"
50#include "serial/serial.h"
51
53class StdSerialIo : public SerialIo {
54public:
55 StdSerialIo(SendMsgFunc send_func, const std::string& port, unsigned baud)
56 : SerialIo(send_func, port, baud) {}
57
58 bool SetOutMsg(const wxString& msg) override;
59 void Start() override;
60 DriverStats GetStats() const override;
61
62private:
63 serial::Serial m_serial;
64 OutputBuffer m_out_que;
65 void* Entry();
66 void Reconnect();
67
68 bool OpenComPortPhysical(const wxString& com_name, unsigned baud_rate);
69 void CloseComPortPhysical();
70 ssize_t WriteComPortPhysical(const char* msg);
71 void RequestStop() override { ThreadCtrl::RequestStop(); }
72};
73
74std::unique_ptr<SerialIo> SerialIo::Create(SendMsgFunc send_msg_func,
75 const std::string& port,
76 unsigned baud) {
77 return std::make_unique<StdSerialIo>(send_msg_func, port, baud);
78}
79
80void* StdSerialIo::Entry() {
81 LineBuffer line_buf;
82
83 // Request the com port from the comm manager
84 if (!OpenComPortPhysical(m_portname, m_baud)) {
85 std::string msg(_("NMEA input device open failed: "));
86 msg += m_portname;
87 CommDriverRegistry::GetInstance().evt_driver_msg.Notify(msg);
88 }
89
90 // The main loop
91 unsigned retries = 0;
92 m_stats.driver_bus = NavAddr::Bus::N0183;
93 m_stats.driver_iface = m_portname.ToStdString();
94 while (KeepGoing()) {
95 unsigned newdata = 0;
96 uint8_t rdata[2000];
97
98 if (m_serial.isOpen()) {
99 try {
100 newdata = m_serial.read(rdata, sizeof(rdata));
101 } catch (std::exception& e) {
102 DEBUG_LOG << "Serial read exception: " << e.what();
103 if (10 < retries++) {
104 // We timed out waiting for the next character 10 times, let's close
105 // the port so that the reconnection logic kicks in and tries to fix
106 // our connection.
107 CloseComPortPhysical();
108 retries = 0;
109 }
110 }
111 } else {
112 // Reconnection logic. Let's try to reopen the port while waiting longer
113 // every time (until we simply keep trying every 2.5 seconds)
114 // std::cerr << "Serial port seems closed." << std::endl;
115 wxMilliSleep(250 * retries);
116 CloseComPortPhysical();
117 if (OpenComPortPhysical(m_portname, m_baud))
118 retries = 0;
119 else if (retries < 10)
120 retries++;
121 }
122
123 // Handle received data
124 for (unsigned i = 0; i < newdata; i++) line_buf.Put(rdata[i]);
125 while (KeepGoing() && line_buf.HasLine()) {
126 auto line = line_buf.GetLine();
127 {
128 std::lock_guard lock(m_stats_mutex);
129 m_stats.tx_count += line.size();
130 }
131 m_send_msg_func(line);
132 }
133
134 // Handle pending output messages
135 std::string qmsg;
136 while (KeepGoing() && m_out_que.Get(qmsg)) {
137 qmsg += "\r\n";
138 bool failed_write = WriteComPortPhysical(qmsg.c_str()) == -1;
139 if (!failed_write) {
140 std::lock_guard lock(m_stats_mutex);
141 m_stats.tx_count += qmsg.size();
142 }
143 if (failed_write && 10 < retries++) {
144 // We failed to write the port 10 times, let's close the port so that
145 // the reconnection logic kicks in and tries to fix our connection.
146 retries = 0;
147 CloseComPortPhysical();
148 }
149 }
150 }
151 CloseComPortPhysical();
152 SignalExit();
153 return nullptr;
154}
155
157 std::thread t([&] { Entry(); });
158 t.detach();
159}
160
161bool StdSerialIo::SetOutMsg(const wxString& msg) {
162 if (msg.size() < 6 || (msg[0] != '$' && msg[0] != '!')) return false;
163 m_out_que.Put(msg.ToStdString());
164 return true;
165}
166
168 std::lock_guard lock(m_stats_mutex);
169 return m_stats;
170}
171
172bool StdSerialIo::OpenComPortPhysical(const wxString& com_name,
173 unsigned baud_rate) {
174 try {
175 m_serial.setPort(com_name.ToStdString());
176 m_serial.setBaudrate(baud_rate);
177 m_serial.open();
178 m_serial.setTimeout(250, 250, 0, 250, 0);
179 } catch (std::exception& e) {
180 auto msg = std::string("Unhandled Exception while opening serial port: ");
181 m_open_log_filter.Log(msg + e.what());
182 }
183 {
184 std::lock_guard lock(m_stats_mutex);
185 m_stats.available = m_serial.isOpen();
186 }
187 return m_serial.isOpen();
188}
189
190void StdSerialIo::CloseComPortPhysical() {
191 try {
192 m_serial.close();
193 } catch (std::exception& e) {
194 MESSAGE_LOG << "Unhandled Exception while closing serial port: "
195 << e.what();
196 }
197}
198
199ssize_t StdSerialIo::WriteComPortPhysical(const char* msg) {
200 if (m_serial.isOpen()) {
201 try {
202 return static_cast<ssize_t>(m_serial.write((uint8_t*)msg, strlen(msg)));
203 } catch (std::exception& e) {
204 DEBUG_LOG << "Unhandled Exception while writing to serial port: "
205 << e.what();
206 return -1;
207 }
208 } else {
209 return -1;
210 }
211}
212
EventVar evt_driver_msg
Notified for messages from drivers.
const void Notify()
Notify all listeners, no data supplied.
Assembles input characters to lines.
void Put(uint8_t ch)
Add a single character.
std::vector< uint8_t > GetLine()
Retrieve a line from buffer, return empty line if none available.
bool HasLine() const
Return true if a line is available to be returned by GetLine().
Synchronized buffer for outbound, line oriented data.
void Put(const std::string line)
Insert line in buffer.
bool Get(std::string &line)
Retrieve a line in buffer.
Nmea0183 serial communications wrapper, possibly running a thread.
Definition serial_io.h:54
static std::unique_ptr< SerialIo > Create(SendMsgFunc send_msg_func, const std::string &port, unsigned baud)
Factory.
virtual void RequestStop() override=0
Request that thread stops operation.
SerialIo implementation based on serial/serial.h.
bool SetOutMsg(const wxString &msg) override
Send a message to remote peer.
DriverStats GetStats() const override
Retrieve updated driver statistics.
void Start() override
Start IO operations including input, possibly in separate thread.
void SignalExit()
Signal that thread has exited.
bool KeepGoing() const
If true continue thread operation, else exit and invoke SignalExit()
virtual void RequestStop()
Request that thread stops operation.
void Log(const std::string &message)
Log a repeated message after interval seconds.
Definition logger.cpp:152
Line-oriented input/output buffers.
Driver registration container, a singleton.
Enhanced logging interface on top of wx/log.h.
Abstract N0183 serial communications interface.
Driver statistics report.
unsigned tx_count
Number of bytes sent since program start.