Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #include "wirestead/builder/serial_builder.hpp"
18 : :
19 : : #include <algorithm>
20 : : #include <boost/asio/io_context.hpp>
21 : : #include <cctype>
22 : :
23 : : #include "wirestead/base/constants.hpp"
24 : : #include "wirestead/builder/auto_initializer.hpp"
25 : : #include "wirestead/config/validation.hpp"
26 : : #include "wirestead/diagnostics/exceptions.hpp"
27 : : #include "wirestead/util/input_validator.hpp"
28 : :
29 : : namespace wirestead {
30 : : namespace builder {
31 : :
32 : 29 : SerialBuilder::SerialBuilder(const std::string& device, uint32_t baud_rate)
33 : 29 : : device_(device),
34 : 29 : baud_rate_(baud_rate),
35 : 29 : auto_start_(false),
36 : 29 : independent_context_(false),
37 : 29 : shared_context_(false),
38 : 29 : retry_interval_ms_(base::constants::DEFAULT_RETRY_INTERVAL_MS),
39 : 29 : char_size_(8),
40 : 29 : stop_bits_(1),
41 : 29 : parity_(config::SerialConfig::Parity::None),
42 : 29 : flow_(config::SerialConfig::Flow::None),
43 : 29 : reopen_on_error_(true) {
44 [ + + + - : 35 : if (device.empty()) throw diagnostics::BuilderException("Device path cannot be empty");
+ - + - +
- ]
45 : :
46 : : // Ensure background IO service is running
47 : 28 : AutoInitializer::ensure_io_context_running();
48 : 30 : }
49 : :
50 : 22 : std::unique_ptr<wrapper::Serial> SerialBuilder::build() {
51 : 22 : std::unique_ptr<wrapper::Serial> serial;
52 [ + + ]: 22 : if (independent_context_) {
53 : 1 : serial = std::make_unique<wrapper::Serial>(device_, baud_rate_, std::make_shared<boost::asio::io_context>());
54 : 1 : serial->manage_external_context(true);
55 : : } else {
56 : 21 : serial = std::make_unique<wrapper::Serial>(device_, baud_rate_);
57 : : }
58 [ + + + - ]: 22 : if (shared_context_) serial->shared_context(true);
59 : :
60 [ + + + - : 22 : if (this->on_data_) serial->on_data(this->on_data_);
+ - ]
61 [ + + + - : 22 : if (this->on_data_batch_) serial->on_data_batch(this->on_data_batch_);
+ - ]
62 [ + + + - : 22 : if (this->on_connect_) serial->on_connect(this->on_connect_);
+ - ]
63 [ + + + - : 22 : if (this->on_disconnect_) serial->on_disconnect(this->on_disconnect_);
+ - ]
64 [ + + + - : 22 : if (this->on_error_) serial->on_error(this->on_error_);
+ - ]
65 [ + + + - : 22 : if (this->on_backpressure_) serial->on_backpressure(this->on_backpressure_);
+ - ]
66 : :
67 [ + + + - ]: 22 : if (char_size_set_) serial->data_bits(static_cast<int>(char_size_));
68 [ + + + - ]: 22 : if (stop_bits_set_) serial->stop_bits(static_cast<int>(stop_bits_));
69 : :
70 : : // Note: wrapper::Serial setters use strings for enum types
71 [ + + ]: 22 : if (parity_set_) {
72 : 4 : std::string p_str = "none";
73 [ + + ]: 4 : if (parity_ == config::SerialConfig::Parity::Even)
74 : 2 : p_str = "even";
75 [ + + ]: 2 : else if (parity_ == config::SerialConfig::Parity::Odd)
76 : 1 : p_str = "odd";
77 : 4 : serial->parity(p_str);
78 : 4 : }
79 : :
80 [ + + ]: 22 : if (flow_set_) {
81 : 4 : std::string f_str = "none";
82 [ + + ]: 4 : if (flow_ == config::SerialConfig::Flow::Software)
83 : 1 : f_str = "software";
84 [ + + ]: 3 : else if (flow_ == config::SerialConfig::Flow::Hardware)
85 : 2 : f_str = "hardware";
86 : 4 : serial->flow_control(f_str);
87 : 4 : }
88 : :
89 [ + + + - ]: 22 : if (reopen_on_error_set_) serial->reopen_on_error(reopen_on_error_);
90 [ + + + - ]: 22 : if (read_chunk_set_) serial->read_chunk(read_chunk_);
91 [ - + - - ]: 22 : if (low_latency_set_) serial->low_latency(low_latency_);
92 [ - + ]: 22 : if (rs485_.enabled) {
93 : 0 : serial->rs485(rs485_.rts_on_send, rs485_.rx_during_tx, rs485_.delay_rts_before_send_ms,
94 : : rs485_.delay_rts_after_send_ms);
95 : : }
96 [ + + + - ]: 22 : if (dtr_) serial->dtr(*dtr_);
97 [ + + + + ]: 22 : if (keep_dtr_on_close_) serial->keep_dtr_on_close();
98 [ - + - - ]: 20 : if (rts_) serial->rts(*rts_);
99 [ + + + - ]: 20 : if (rx_idle_timeout_set_) serial->rx_idle_timeout(rx_idle_timeout_);
100 [ + + + - ]: 20 : if (retry_interval_set_) serial->retry_interval(std::chrono::milliseconds(retry_interval_ms_));
101 : :
102 [ + + + - ]: 20 : if (this->bp_strategy_set_) serial->backpressure_strategy(this->bp_strategy_);
103 : 20 : serial->backpressure_threshold(this->get_effective_backpressure_threshold());
104 : :
105 [ + + ]: 20 : if (this->framer_factory_) {
106 : 1 : serial->framer(this->framer_factory_());
107 : : }
108 [ + + ]: 20 : if (this->on_message_) {
109 : 1 : serial->on_message(this->on_message_);
110 : : }
111 [ + + ]: 20 : if (this->on_message_batch_) {
112 : 1 : serial->on_message_batch(this->on_message_batch_);
113 : : }
114 : :
115 [ + + ]: 20 : if (auto_start_) {
116 : 1 : serial->auto_start(true);
117 : : }
118 : :
119 : 20 : return serial;
120 : 2 : }
121 : :
122 : 2 : SerialBuilder& SerialBuilder::auto_start(bool auto_start) {
123 : 2 : auto_start_ = auto_start;
124 : 2 : return *this;
125 : : }
126 : :
127 : 4 : SerialBuilder& SerialBuilder::char_size(unsigned int size) {
128 : 4 : char_size_ = size;
129 : 4 : char_size_set_ = true;
130 : 4 : return *this;
131 : : }
132 : :
133 : 3 : SerialBuilder& SerialBuilder::stop_bits(unsigned int bits) {
134 : 3 : config::detail::range(bits, base::constants::MIN_STOP_BITS, base::constants::MAX_STOP_BITS, "invalid stop_bits");
135 : 3 : stop_bits_ = bits;
136 : 3 : stop_bits_set_ = true;
137 : 3 : return *this;
138 : : }
139 : :
140 : 1 : SerialBuilder& SerialBuilder::parity(config::SerialConfig::Parity p) {
141 [ + - - + : 1 : config::detail::require(p == config::SerialConfig::Parity::None || p == config::SerialConfig::Parity::Even ||
- - ]
142 : : p == config::SerialConfig::Parity::Odd,
143 : : "invalid parity");
144 : 1 : parity_ = p;
145 : 1 : parity_set_ = true;
146 : 1 : return *this;
147 : : }
148 : :
149 : 7 : SerialBuilder& SerialBuilder::parity(const std::string& p) {
150 : 7 : config::detail::text_option(p, {"none", "even", "odd"}, "invalid parity");
151 : 4 : std::string value = p;
152 : 4 : std::transform(value.begin(), value.end(), value.begin(),
153 : 15 : [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
154 : :
155 [ + - + + ]: 4 : if (value == "even") {
156 : 2 : parity_ = config::SerialConfig::Parity::Even;
157 [ + - + + ]: 2 : } else if (value == "odd") {
158 : 1 : parity_ = config::SerialConfig::Parity::Odd;
159 : : } else {
160 : 1 : parity_ = config::SerialConfig::Parity::None;
161 : : }
162 : 4 : parity_set_ = true;
163 : 4 : return *this;
164 : 4 : }
165 : :
166 : 1 : SerialBuilder& SerialBuilder::flow_control(config::SerialConfig::Flow f) {
167 [ + - + - : 1 : config::detail::require(f == config::SerialConfig::Flow::None || f == config::SerialConfig::Flow::Software ||
+ - ]
168 : : f == config::SerialConfig::Flow::Hardware,
169 : : "invalid flow_control");
170 : 1 : flow_ = f;
171 : 1 : flow_set_ = true;
172 : 1 : return *this;
173 : : }
174 : :
175 : 5 : SerialBuilder& SerialBuilder::flow_control(const std::string& f) {
176 : 5 : config::detail::text_option(f, {"none", "software", "hardware"}, "invalid flow control");
177 : 4 : std::string value = f;
178 : 4 : std::transform(value.begin(), value.end(), value.begin(),
179 : 28 : [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
180 : :
181 [ + - + + ]: 4 : if (value == "software") {
182 : 1 : flow_ = config::SerialConfig::Flow::Software;
183 [ + - + + ]: 3 : } else if (value == "hardware") {
184 : 2 : flow_ = config::SerialConfig::Flow::Hardware;
185 : : } else {
186 : 1 : flow_ = config::SerialConfig::Flow::None;
187 : : }
188 : 4 : flow_set_ = true;
189 : 4 : return *this;
190 : 4 : }
191 : :
192 : 3 : SerialBuilder& SerialBuilder::read_chunk(size_t bytes) {
193 : 3 : config::detail::range(bytes, base::constants::MIN_READ_BUFFER_SIZE, base::constants::MAX_READ_BUFFER_SIZE,
194 : : "invalid read_chunk");
195 : 1 : read_chunk_ = bytes;
196 : 1 : read_chunk_set_ = true;
197 : 1 : return *this;
198 : : }
199 : :
200 : 0 : SerialBuilder& SerialBuilder::low_latency(bool enable) {
201 : 0 : low_latency_ = enable;
202 : 0 : low_latency_set_ = true;
203 : 0 : return *this;
204 : : }
205 : :
206 : 0 : SerialBuilder& SerialBuilder::rs485(bool rts_on_send, bool rx_during_tx, unsigned delay_before_ms,
207 : : unsigned delay_after_ms) {
208 : 0 : rs485_.enabled = true;
209 : 0 : rs485_.rts_on_send = rts_on_send;
210 : 0 : rs485_.rx_during_tx = rx_during_tx;
211 : 0 : rs485_.delay_rts_before_send_ms = delay_before_ms;
212 : 0 : rs485_.delay_rts_after_send_ms = delay_after_ms;
213 : 0 : return *this;
214 : : }
215 : :
216 : 2 : SerialBuilder& SerialBuilder::dtr(bool assert_line) {
217 : 2 : dtr_ = assert_line;
218 : 2 : return *this;
219 : : }
220 : :
221 : 3 : SerialBuilder& SerialBuilder::keep_dtr_on_close(bool enable) {
222 : 3 : keep_dtr_on_close_ = enable;
223 : 3 : return *this;
224 : : }
225 : :
226 : 0 : SerialBuilder& SerialBuilder::rts(bool assert_line) {
227 : 0 : rts_ = assert_line;
228 : 0 : return *this;
229 : : }
230 : :
231 : 1 : SerialBuilder& SerialBuilder::rx_idle_timeout(std::chrono::milliseconds timeout) {
232 : 1 : config::detail::duration(timeout, base::constants::MIN_IDLE_TIMEOUT_MS, base::constants::MAX_IDLE_TIMEOUT_MS, true,
233 : : "invalid rx_idle_timeout");
234 : 1 : rx_idle_timeout_ = timeout;
235 : 1 : rx_idle_timeout_set_ = true;
236 : 1 : return *this;
237 : : }
238 : :
239 : 6 : SerialBuilder& SerialBuilder::reopen_on_error(bool enable) {
240 : 6 : reopen_on_error_ = enable;
241 : 6 : reopen_on_error_set_ = true;
242 : 6 : return *this;
243 : : }
244 : :
245 : 3 : SerialBuilder& SerialBuilder::retry_interval(std::chrono::milliseconds interval) {
246 : 3 : config::detail::duration(interval, base::constants::MIN_RETRY_INTERVAL_MS, base::constants::MAX_RETRY_INTERVAL_MS,
247 : : false, "invalid retry_interval");
248 : 3 : retry_interval_ms_ = static_cast<uint32_t>(interval.count());
249 : 3 : retry_interval_set_ = true;
250 : 3 : return *this;
251 : : }
252 : :
253 : 1 : SerialBuilder& SerialBuilder::independent_context(bool use_independent) {
254 : 1 : independent_context_ = use_independent;
255 : 1 : return *this;
256 : : }
257 : :
258 : 2 : SerialBuilder& SerialBuilder::shared_context(bool use_shared) {
259 : 2 : shared_context_ = use_shared;
260 : 2 : return *this;
261 : : }
262 : :
263 : : // Explicit template instantiations
264 : :
265 : : } // namespace builder
266 : : } // namespace wirestead
|