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, see <https://www.gnu.org/licenses/ *
17 **************************************************************************/
18
26#include <memory>
27#include <string>
28
29// For compilers that support precompilation, includes "wx.h".
30#include <wx/wxprec.h>
31
32#ifndef WX_PRECOMP
33#include <wx/wx.h>
34#endif
35
36#include <mutex> // std::mutex
37#include <queue> // std::queue
38#include <thread>
39#include <vector>
40
41#include <wx/log.h>
42#include <wx/string.h>
43#include <wx/utils.h>
44
45#include "model/comm_buffers.h"
47#include "model/logger.h"
48#include "model/serial_io.h"
49#include "serial/serial.h"
50
52class StdSerialIo : public SerialIo {
53public:
54 StdSerialIo(SendMsgFunc send_func, const std::string& port, unsigned baud)
55 : SerialIo(send_func, port, baud) {}
56
57 bool SetOutMsg(const wxString& msg) override;
58 void Start() override;
59 DriverStats GetStats() const override;
60
61private:
62 serial::Serial m_serial;
63 OutputBuffer m_out_que;
64 void* Entry();
65 void Reconnect();
66
67 bool OpenComPortPhysical(const wxString& com_name, unsigned baud_rate);
68 void CloseComPortPhysical();
69 ssize_t WriteComPortPhysical(const char* msg);
70 void RequestStop() override { ThreadCtrl::RequestStop(); }
71};
72
73std::unique_ptr<SerialIo> SerialIo::Create(SendMsgFunc send_msg_func,
74 const std::string& port,
75 unsigned baud) {
76 return std::make_unique<StdSerialIo>(send_msg_func, port, baud);
77}
78
79void* StdSerialIo::Entry() {
80 LineBuffer line_buf;
81
82 // Request the com port from the comm manager
83 if (!OpenComPortPhysical(m_portname, m_baud)) {
84 std::string msg(_("NMEA input device open failed: "));
85 msg += m_portname;
86 CommDriverRegistry::GetInstance().evt_driver_msg.Notify(msg);
87 }
88
89 // The main loop
90 unsigned retries = 0;
91 m_stats.driver_bus = NavAddr::Bus::N0183;
92 m_stats.driver_iface = m_portname.ToStdString();
93 while (KeepGoing()) {
94 unsigned newdata = 0;
95 uint8_t rdata[2000];
96
97 if (m_serial.isOpen()) {
98 try {
99 newdata = m_serial.read(rdata, sizeof(rdata));
100 } catch (std::exception& e) {
101 DEBUG_LOG << "Serial read exception: " << e.what();
102 if (10 < retries++) {
103 // We timed out waiting for the next character 10 times, let's close
104 // the port so that the reconnection logic kicks in and tries to fix
105 // our connection.
106 CloseComPortPhysical();
107 retries = 0;
108 }
109 }
110 } else {
111 // Reconnection logic. Let's try to reopen the port while waiting longer
112 // every time (until we simply keep trying every 2.5 seconds)
113 // std::cerr << "Serial port seems closed." << std::endl;
114 wxMilliSleep(250 * retries);
115 CloseComPortPhysical();
116 if (OpenComPortPhysical(m_portname, m_baud))
117 retries = 0;
118 else if (retries < 10)
119 retries++;
120 }
121
122 // Handle received data
123 for (unsigned i = 0; i < newdata; i++) line_buf.Put(rdata[i]);
124 while (KeepGoing() && line_buf.HasLine()) {
125 auto line = line_buf.GetLine();
126 {
127 std::lock_guard lock(m_stats_mutex);
128 m_stats.rx_count += line.size();
129 }
130 m_send_msg_func(line);
131 }
132
133 // Handle pending output messages
134 std::string qmsg;
135 while (KeepGoing() && m_out_que.Get(qmsg)) {
136 if (qmsg.size() < 3) continue;
137 if (qmsg.find("\r\n", qmsg.size() - 2) == std::string::npos)
138 qmsg += "\r\n";
139 bool failed_write = WriteComPortPhysical(qmsg.c_str()) == -1;
140 if (!failed_write) {
141 std::lock_guard lock(m_stats_mutex);
142 m_stats.tx_count += qmsg.size();
143 }
144 if (failed_write && 10 < retries++) {
145 // We failed to write the port 10 times, let's close the port so that
146 // the reconnection logic kicks in and tries to fix our connection.
147 retries = 0;
148 CloseComPortPhysical();
149 }
150 }
151 }
152 CloseComPortPhysical();
153 SignalExit();
154 return nullptr;
155}
156
158 std::thread t([&] { Entry(); });
159 t.detach();
160}
161
162bool StdSerialIo::SetOutMsg(const wxString& msg) {
163 if (msg.size() < 6 || (msg[0] != '$' && msg[0] != '!')) return false;
164 m_out_que.Put(msg.ToStdString());
165 return true;
166}
167
169 std::lock_guard lock(m_stats_mutex);
170 return m_stats;
171}
172
173bool StdSerialIo::OpenComPortPhysical(const wxString& com_name,
174 unsigned baud_rate) {
175 try {
176 m_serial.setPort(com_name.ToStdString());
177 m_serial.setBaudrate(baud_rate);
178 m_serial.open();
179 m_serial.setTimeout(250, 250, 0, 250, 0);
180 } catch (std::exception& e) {
181 auto msg = std::string("Unhandled Exception while opening serial port: ");
182 m_open_log_filter.Log(msg + e.what());
183 }
184 {
185 std::lock_guard lock(m_stats_mutex);
186 m_stats.available = m_serial.isOpen();
187 }
188 return m_serial.isOpen();
189}
190
191void StdSerialIo::CloseComPortPhysical() {
192 try {
193 m_serial.close();
194 } catch (std::exception& e) {
195 MESSAGE_LOG << "Unhandled Exception while closing serial port: "
196 << e.what();
197 }
198}
199
200ssize_t StdSerialIo::WriteComPortPhysical(const char* msg) {
201 if (m_serial.isOpen()) {
202 try {
203 return static_cast<ssize_t>(m_serial.write((uint8_t*)msg, strlen(msg)));
204 } catch (std::exception& e) {
205 DEBUG_LOG << "Unhandled Exception while writing to serial port: "
206 << e.what();
207 return -1;
208 }
209 } else {
210 return -1;
211 }
212}
213
EventVar evt_driver_msg
Notified for messages from drivers.
void Notify() override
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:151
Class that provides a portable serial port interface.
Definition serial.h:147
size_t read(uint8_t *buffer, size_t size)
Read a given amount of bytes from the serial port into a given buffer.
void setPort(const std::string &port)
Sets the serial port identifier.
size_t write(const uint8_t *data, size_t size)
Write a string to the serial port.
void setBaudrate(uint32_t baudrate)
Sets the baudrate for the serial port.
void close()
Closes the serial port.
void setTimeout(Timeout &timeout)
Sets the timeout for reads and writes using the Timeout struct.
void open()
Opens the serial port as long as the port is set and the port isn't already open.
bool isOpen() const
Gets the open status of the serial port.
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.
unsigned rx_count
Number of bytes received since program start.