LCOV - code coverage report
Current view: top level - wirestead/builder - serial_builder.cc (source / functions) Coverage Total Hit
Test: Wirestead Coverage Report Lines: 90.7 % 161 146
Test Date: 2026-10-29 11:36:05 Functions: 86.4 % 22 19
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 70.5 % 146 103

             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
        

Generated by: LCOV version 2.0-1