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 qmsg += "\r\n";
137 bool failed_write = WriteComPortPhysical(qmsg.c_str()) == -1;
138 if (!failed_write) {
139 std::lock_guard lock(m_stats_mutex);
140 m_stats.tx_count += qmsg.size();
141 }
142 if (failed_write && 10 < retries++) {
143 // We failed to write the port 10 times, let's close the port so that
144 // the reconnection logic kicks in and tries to fix our connection.
145 retries = 0;
146 CloseComPortPhysical();
147 }
148 }
149 }
150 CloseComPortPhysical();
151 SignalExit();
152 return nullptr;
153}
154
156 std::thread t([&] { Entry(); });
157 t.detach();
158}
159
160bool StdSerialIo::SetOutMsg(const wxString& msg) {
161 if (msg.size() < 6 || (msg[0] != '$' && msg[0] != '!')) return false;
162 m_out_que.Put(msg.ToStdString());
163 return true;
164}
165
167 std::lock_guard lock(m_stats_mutex);
168 return m_stats;
169}
170
171bool StdSerialIo::OpenComPortPhysical(const wxString& com_name,
172 unsigned baud_rate) {
173 try {
174 m_serial.setPort(com_name.ToStdString());
175 m_serial.setBaudrate(baud_rate);
176 m_serial.open();
177 m_serial.setTimeout(250, 250, 0, 250, 0);
178 } catch (std::exception& e) {
179 auto msg = std::string("Unhandled Exception while opening serial port: ");
180 m_open_log_filter.Log(msg + e.what());
181 }
182 {
183 std::lock_guard lock(m_stats_mutex);
184 m_stats.available = m_serial.isOpen();
185 }
186 return m_serial.isOpen();
187}
188
189void StdSerialIo::CloseComPortPhysical() {
190 try {
191 m_serial.close();
192 } catch (std::exception& e) {
193 MESSAGE_LOG << "Unhandled Exception while closing serial port: "
194 << e.what();
195 }
196}
197
198ssize_t StdSerialIo::WriteComPortPhysical(const char* msg) {
199 if (m_serial.isOpen()) {
200 try {
201 return static_cast<ssize_t>(m_serial.write((uint8_t*)msg, strlen(msg)));
202 } catch (std::exception& e) {
203 DEBUG_LOG << "Unhandled Exception while writing to serial port: "
204 << e.what();
205 return -1;
206 }
207 } else {
208 return -1;
209 }
210}
211
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.