scc 2026.07
SystemC components library
serialized_connection.h
1/*******************************************************************************
2 * Copyright 2026 MINRES Technologies GmbH
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#ifndef TLM_SCC_TCP4TLM_SERIALIZED_CONNECTION_H_
18#define TLM_SCC_TCP4TLM_SERIALIZED_CONNECTION_H_
19
20#include <boost/asio.hpp>
21#include <cstdint>
22#include <iomanip>
23#include <sstream>
24#include <string>
25#include <util/logging.h>
26#include <vector>
27
28namespace scc {
29namespace tcp4tlm {
30
32
38template <typename TSEND, typename TREC> class connection : public std::enable_shared_from_this<connection<TSEND, TREC>> {
39public:
40#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) && defined(USE_UDS)
41#warning "This build uses stream sockets and will not work across the network"
42 typedef boost::asio::local::stream_protocol::socket socket_t;
43 typedef boost::asio::local::stream_protocol::endpoint endpoint_t;
44 typedef boost::asio::local::stream_protocol::acceptor acceptor_t;
45#elif defined(USE_UDP)
46 typedef boost::asio::ip::udp::socket socket_t;
47 typedef boost::asio::ip::udp::endpoint endpoint_t;
48#else
49 typedef boost::asio::ip::tcp::socket socket_t;
50 typedef boost::asio::ip::tcp::endpoint endpoint_t;
51 typedef boost::asio::ip::tcp::acceptor acceptor_t;
52#endif
53 typedef std::shared_ptr<connection<TSEND, TREC>> ptr;
54
55 struct async_listener : public std::enable_shared_from_this<async_listener> {
56 virtual void send_completed(const boost::system::error_code& error) = 0;
57 virtual void receive_completed(const boost::system::error_code& error, const TREC* const result) = 0;
58 };
59
60 connection(boost::asio::io_context& io_context)
61 : socket_(io_context) {}
62
63 socket_t& socket() { return socket_; }
64 void add_listener(std::shared_ptr<async_listener> l) { listener = l; }
65
66 void async_write(TSEND& t) { async_write(&t); }
67
68 void async_write(TSEND* t) {
69 if(!prepare_outbound_data(t)) {
70 boost::system::error_code err(boost::asio::error::invalid_argument);
71 if(listener)
72 listener->send_completed(err);
73 return;
74 }
75
76 std::vector<boost::asio::const_buffer> buffers;
77 buffers.push_back(boost::asio::buffer(outbound_header_));
78 buffers.push_back(boost::asio::buffer(outbound_data_));
79 auto self = this->shared_from_this();
80 boost::asio::async_write(socket_, buffers, [self](const boost::system::error_code& error, std::size_t bytes_transferred) {
81 self->handle_async_write(error, bytes_transferred);
82 });
83 }
84
85protected:
86 void handle_async_write(const boost::system::error_code& err, size_t /*bytes_transferred*/) {
87 if(listener != NULL)
88 listener->send_completed(err);
89 }
90
91public:
92 void async_read() {
93 auto self = this->shared_from_this();
94 boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
95 [self](const boost::system::error_code& error, std::size_t) { self->async_read_header(error); });
96 }
97
98protected:
99 void async_read_header(const boost::system::error_code& e) {
100 if(e) {
101 if(listener)
102 listener->receive_completed(e, NULL);
103 } else {
104 std::string header(inbound_header_, header_length);
105 std::istringstream is(header);
106 std::size_t inbound_data_size = 0;
107 is >> std::hex >> inbound_data_size;
108 if(inbound_data_size == 0) {
109 boost::system::error_code error(boost::asio::error::invalid_argument);
110 if(listener)
111 listener->receive_completed(error, NULL);
112 return;
113 }
114 inbound_data_.resize(inbound_data_size);
115 auto self = this->shared_from_this();
116 boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_),
117 [self](const boost::system::error_code& error, std::size_t) { self->async_read_data(error); });
118 }
119 }
120
121 void async_read_data(const boost::system::error_code& e) {
122 if(e) {
123 if(listener)
124 listener->receive_completed(e, NULL);
125 } else {
126 if(!TREC::verify(inbound_data_.data(), inbound_data_.size())) {
127 boost::system::error_code err(boost::asio::error::invalid_argument);
128 if(listener)
129 listener->receive_completed(err, NULL);
130 return;
131 }
132#ifdef TRACE_COMMUNICATION
133 std::string raw_data(reinterpret_cast<const char*>(inbound_data_.data()), inbound_data_.size());
134 CPPLOG(TRACE, "tcp4tlm") << "inbound async data (" << inbound_data_.size() << "):[" << raw_data << "]" << std::endl;
135#endif
136 TREC* t = new TREC(std::move(inbound_data_));
137 if(listener)
138 listener->receive_completed(e, t);
139 delete t;
140 }
141 }
142
143public:
144 void write_data(std::shared_ptr<TSEND>& t) { write_data(t.get()); }
145
146 void write_data(TSEND& t) { write_data(&t); }
147
148 void write_data(TSEND* t) {
149 boost::system::error_code ec;
150 this->write_data(t, ec);
151 boost::asio::detail::throw_error(ec);
152 }
153
154 void write_data(TSEND* t, boost::system::error_code& ec) {
155 if(!prepare_outbound_data(t)) {
156 ec.assign(boost::asio::error::invalid_argument, boost::asio::error::get_system_category());
157 return;
158 }
159#ifdef GENERATE_STATISTICS
160 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &get_t_stamp());
161#endif
162 std::vector<boost::asio::const_buffer> buffers;
163 buffers.push_back(boost::asio::buffer(outbound_header_));
164 buffers.push_back(boost::asio::buffer(outbound_data_));
165 boost::asio::write(socket_, buffers);
166 }
167
168 bool read_data_available() {
169 boost::asio::socket_base::bytes_readable command(true);
170 socket_.io_control(command);
171 std::size_t bytes_readable = command.get();
172 return bytes_readable > 0;
173 }
174
175 void read_data(std::shared_ptr<TREC>& msg) {
176 TREC* m = nullptr;
177 read_data(m);
178 msg.reset(m);
179 }
180
181 void read_data(TREC*& t) {
182 boost::system::error_code ec;
183 this->read_data(t, ec);
184 boost::asio::detail::throw_error(ec);
185 }
186
187 void read_data(TREC*& t, boost::system::error_code& ec) {
188 boost::asio::read(socket_, boost::asio::buffer(inbound_header_, header_length), boost::asio::transfer_exactly(header_length));
189 std::string header(inbound_header_, header_length);
190 std::istringstream is(header);
191 std::size_t inbound_data_size = 0;
192 is >> std::hex >> inbound_data_size;
193 if(inbound_data_size == 0) {
194 ec.assign(boost::asio::error::invalid_argument, boost::asio::error::get_system_category());
195 return;
196 }
197
198 inbound_data_.resize(inbound_data_size);
199 boost::asio::read(socket_, boost::asio::buffer(inbound_data_, inbound_data_size), boost::asio::transfer_exactly(inbound_data_size));
200 if(!TREC::verify(inbound_data_.data(), inbound_data_.size())) {
201 ec.assign(boost::asio::error::invalid_argument, boost::asio::error::get_system_category());
202 return;
203 }
204#ifdef TRACE_COMMUNICATION
205 std::string raw_data(reinterpret_cast<const char*>(inbound_data_.data()), inbound_data_.size());
206 CPPLOG(TRACE, "tcp4tlm") << "inbound sync data (" << inbound_data_.size() << "):[" << raw_data << "]" << std::endl;
207#endif
208 t = new TREC(std::move(inbound_data_));
209 }
210
211#ifdef GENERATE_STATISTICS
212 static timespec& get_t_stamp() {
213 static timespec tstamp;
214 return tstamp;
215 }
216#endif
217
218private:
219 bool prepare_outbound_data(TSEND* t) {
220 if(t == nullptr || !(*t) || t->size() == 0) {
221 return false;
222 }
223
224 outbound_data_.assign(reinterpret_cast<const char*>(t->data()), reinterpret_cast<const char*>(t->data()) + t->size());
225#ifdef TRACE_COMMUNICATION
226 CPPLOG(TRACE, "tcp4tlm") << "outbound data (" << outbound_data_.size() << "):[" << outbound_data_ << "]" << std::endl;
227#endif
228 std::ostringstream header_stream;
229 header_stream << std::setw(header_length) << std::hex << std::setfill('0') << outbound_data_.size();
230 if(!header_stream || header_stream.str().size() != header_length) {
231 return false;
232 }
233 outbound_header_ = header_stream.str();
234 return true;
235 }
236
237 socket_t socket_;
238 enum { header_length = 8 };
239 std::string outbound_header_;
240 std::string outbound_data_;
241 char inbound_header_[header_length];
242 std::vector<uint8_t> inbound_data_;
243 std::shared_ptr<async_listener> listener;
244};
245
246} // namespace tcp4tlm
247} // namespace scc
248
249#endif // TLM_SCC_TCP4TLM_SERIALIZED_CONNECTION_H_
SCC TLM utilities.