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/diagnostics/exceptions.hpp"
26 : :
27 : : namespace wirestead {
28 : : namespace builder {
29 : :
30 : 22 : SerialBuilder::SerialBuilder(const std::string& device, uint32_t baud_rate)
31 : 22 : : device_(device),
32 : 22 : baud_rate_(baud_rate),
33 : 22 : auto_start_(false),
34 : 22 : independent_context_(false),
35 : 22 : shared_context_(false),
36 : 22 : retry_interval_ms_(base::constants::DEFAULT_RETRY_INTERVAL_MS),
37 : 22 : char_size_(8),
38 : 22 : stop_bits_(1),
39 : 22 : parity_(config::SerialConfig::Parity::None),
40 : 22 : flow_(config::SerialConfig::Flow::None),
41 : 22 : reopen_on_error_(true) {
42 [ + + + - : 28 : if (device.empty()) throw diagnostics::BuilderException("Device path cannot be empty");
+ - + - +
- ]
43 : :
44 : : // Ensure background IO service is running
45 : 21 : AutoInitializer::ensure_io_context_running();
46 : 23 : }
47 : :
48 : 21 : std::unique_ptr<wrapper::Serial> SerialBuilder::build() {
49 : 21 : std::unique_ptr<wrapper::Serial> serial;
50 [ + + ]: 21 : if (independent_context_) {
51 : 1 : serial = std::make_unique<wrapper::Serial>(device_, baud_rate_, std::make_shared<boost::asio::io_context>());
52 : 1 : serial->manage_external_context(true);
53 : : } else {
54 : 20 : serial = std::make_unique<wrapper::Serial>(device_, baud_rate_);
55 : : }
56 [ + + + - ]: 21 : if (shared_context_) serial->shared_context(true);
57 : :
58 [ + + + - : 21 : if (this->on_data_) serial->on_data(this->on_data_);
+ - ]
59 [ + + + - : 21 : if (this->on_data_batch_) serial->on_data_batch(this->on_data_batch_);
+ - ]
60 [ + + + - : 21 : if (this->on_connect_) serial->on_connect(this->on_connect_);
+ - ]
61 [ + + + - : 21 : if (this->on_disconnect_) serial->on_disconnect(this->on_disconnect_);
+ - ]
62 [ + + + - : 21 : if (this->on_error_) serial->on_error(this->on_error_);
+ - ]
63 [ + + + - : 21 : if (this->on_backpressure_) serial->on_backpressure(this->on_backpressure_);
+ - ]
64 : :
65 [ + + + - ]: 21 : if (char_size_set_) serial->data_bits(static_cast<int>(char_size_));
66 [ + + + - ]: 21 : if (stop_bits_set_) serial->stop_bits(static_cast<int>(stop_bits_));
67 : :
68 : : // Note: wrapper::Serial setters use strings for enum types
69 [ + + ]: 21 : if (parity_set_) {
70 : 6 : std::string p_str = "none";
71 [ + + ]: 6 : if (parity_ == config::SerialConfig::Parity::Even)
72 : 2 : p_str = "even";
73 [ + + ]: 4 : else if (parity_ == config::SerialConfig::Parity::Odd)
74 : 1 : p_str = "odd";
75 : 6 : serial->parity(p_str);
76 : 6 : }
77 : :
78 [ + + ]: 21 : if (flow_set_) {
79 : 5 : std::string f_str = "none";
80 [ + + ]: 5 : if (flow_ == config::SerialConfig::Flow::Software)
81 : 1 : f_str = "software";
82 [ + + ]: 4 : else if (flow_ == config::SerialConfig::Flow::Hardware)
83 : 2 : f_str = "hardware";
84 : 5 : serial->flow_control(f_str);
85 : 5 : }
86 : :
87 [ + + + - ]: 21 : if (reopen_on_error_set_) serial->reopen_on_error(reopen_on_error_);
88 [ + + + - ]: 21 : if (read_chunk_set_) serial->read_chunk(read_chunk_);
89 [ - + - - ]: 21 : if (low_latency_set_) serial->low_latency(low_latency_);
90 [ - + ]: 21 : if (rs485_.enabled) {
91 : 0 : serial->rs485(rs485_.rts_on_send, rs485_.rx_during_tx, rs485_.delay_rts_before_send_ms,
92 : : rs485_.delay_rts_after_send_ms);
93 : : }
94 [ - + - - ]: 21 : if (dtr_) serial->dtr(*dtr_);
95 [ - + - - ]: 21 : if (rts_) serial->rts(*rts_);
96 [ + + + - ]: 21 : if (rx_idle_timeout_set_) serial->rx_idle_timeout(rx_idle_timeout_);
97 [ + + + - ]: 21 : if (retry_interval_set_) serial->retry_interval(std::chrono::milliseconds(retry_interval_ms_));
98 : :
99 [ + + + - ]: 21 : if (this->bp_strategy_set_) serial->backpressure_strategy(this->bp_strategy_);
100 : 21 : serial->backpressure_threshold(this->get_effective_backpressure_threshold());
101 : :
102 [ + + ]: 21 : if (this->framer_factory_) {
103 : 1 : serial->framer(this->framer_factory_());
104 : : }
105 [ + + ]: 21 : if (this->on_message_) {
106 : 1 : serial->on_message(this->on_message_);
107 : : }
108 [ + + ]: 21 : if (this->on_message_batch_) {
109 : 1 : serial->on_message_batch(this->on_message_batch_);
110 : : }
111 : :
112 [ + + ]: 21 : if (auto_start_) {
113 : 1 : serial->auto_start(true);
114 : : }
115 : :
116 : 21 : return serial;
117 : 0 : }
118 : :
119 : 2 : SerialBuilder& SerialBuilder::auto_start(bool auto_start) {
120 : 2 : auto_start_ = auto_start;
121 : 2 : return *this;
122 : : }
123 : :
124 : 4 : SerialBuilder& SerialBuilder::char_size(unsigned int size) {
125 : 4 : char_size_ = size;
126 : 4 : char_size_set_ = true;
127 : 4 : return *this;
128 : : }
129 : :
130 : 3 : SerialBuilder& SerialBuilder::stop_bits(unsigned int bits) {
131 : 3 : stop_bits_ = bits;
132 : 3 : stop_bits_set_ = true;
133 : 3 : return *this;
134 : : }
135 : :
136 : 1 : SerialBuilder& SerialBuilder::parity(config::SerialConfig::Parity p) {
137 : 1 : parity_ = p;
138 : 1 : parity_set_ = true;
139 : 1 : return *this;
140 : : }
141 : :
142 : 5 : SerialBuilder& SerialBuilder::parity(const std::string& p) {
143 : 5 : std::string value = p;
144 : 5 : std::transform(value.begin(), value.end(), value.begin(),
145 : 25 : [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
146 : :
147 [ + - + + ]: 5 : if (value == "even") {
148 : 1 : parity_ = config::SerialConfig::Parity::Even;
149 [ + - + + ]: 4 : } else if (value == "odd") {
150 : 1 : parity_ = config::SerialConfig::Parity::Odd;
151 : : } else {
152 : 3 : parity_ = config::SerialConfig::Parity::None;
153 : : }
154 : 5 : parity_set_ = true;
155 : 5 : return *this;
156 : 5 : }
157 : :
158 : 1 : SerialBuilder& SerialBuilder::flow_control(config::SerialConfig::Flow f) {
159 : 1 : flow_ = f;
160 : 1 : flow_set_ = true;
161 : 1 : return *this;
162 : : }
163 : :
164 : 4 : SerialBuilder& SerialBuilder::flow_control(const std::string& f) {
165 : 4 : std::string value = f;
166 : 4 : std::transform(value.begin(), value.end(), value.begin(),
167 : 27 : [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
168 : :
169 [ + - + + ]: 4 : if (value == "software") {
170 : 1 : flow_ = config::SerialConfig::Flow::Software;
171 [ + - + + ]: 3 : } else if (value == "hardware") {
172 : 1 : flow_ = config::SerialConfig::Flow::Hardware;
173 : : } else {
174 : 2 : flow_ = config::SerialConfig::Flow::None;
175 : : }
176 : 4 : flow_set_ = true;
177 : 4 : return *this;
178 : 4 : }
179 : :
180 : 3 : SerialBuilder& SerialBuilder::read_chunk(size_t bytes) {
181 : 3 : read_chunk_ = bytes;
182 : 3 : read_chunk_set_ = true;
183 : 3 : return *this;
184 : : }
185 : :
186 : 0 : SerialBuilder& SerialBuilder::low_latency(bool enable) {
187 : 0 : low_latency_ = enable;
188 : 0 : low_latency_set_ = true;
189 : 0 : return *this;
190 : : }
191 : :
192 : 0 : SerialBuilder& SerialBuilder::rs485(bool rts_on_send, bool rx_during_tx, unsigned delay_before_ms,
193 : : unsigned delay_after_ms) {
194 : 0 : rs485_.enabled = true;
195 : 0 : rs485_.rts_on_send = rts_on_send;
196 : 0 : rs485_.rx_during_tx = rx_during_tx;
197 : 0 : rs485_.delay_rts_before_send_ms = delay_before_ms;
198 : 0 : rs485_.delay_rts_after_send_ms = delay_after_ms;
199 : 0 : return *this;
200 : : }
201 : :
202 : 0 : SerialBuilder& SerialBuilder::dtr(bool assert_line) {
203 : 0 : dtr_ = assert_line;
204 : 0 : return *this;
205 : : }
206 : :
207 : 0 : SerialBuilder& SerialBuilder::rts(bool assert_line) {
208 : 0 : rts_ = assert_line;
209 : 0 : return *this;
210 : : }
211 : :
212 : 1 : SerialBuilder& SerialBuilder::rx_idle_timeout(std::chrono::milliseconds timeout) {
213 : 1 : rx_idle_timeout_ = timeout;
214 : 1 : rx_idle_timeout_set_ = true;
215 : 1 : return *this;
216 : : }
217 : :
218 : 6 : SerialBuilder& SerialBuilder::reopen_on_error(bool enable) {
219 : 6 : reopen_on_error_ = enable;
220 : 6 : reopen_on_error_set_ = true;
221 : 6 : return *this;
222 : : }
223 : :
224 : 3 : SerialBuilder& SerialBuilder::retry_interval(std::chrono::milliseconds interval) {
225 : 3 : retry_interval_ms_ = static_cast<uint32_t>(interval.count());
226 : 3 : retry_interval_set_ = true;
227 : 3 : return *this;
228 : : }
229 : :
230 : 1 : SerialBuilder& SerialBuilder::independent_context(bool use_independent) {
231 : 1 : independent_context_ = use_independent;
232 : 1 : return *this;
233 : : }
234 : :
235 : 2 : SerialBuilder& SerialBuilder::shared_context(bool use_shared) {
236 : 2 : shared_context_ = use_shared;
237 : 2 : return *this;
238 : : }
239 : :
240 : : // Explicit template instantiations
241 : :
242 : : } // namespace builder
243 : : } // namespace wirestead
|