DESERT 3.5.1
Loading...
Searching...
No Matches
uwmodemcsa.cpp
Go to the documentation of this file.
1//
2// Copyright (c) 2019 Regents of the SIGNET lab, University of Padova.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions
7// are met:
8// 1. Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright
11// notice, this list of conditions and the following disclaimer in the
12// documentation and/or other materials provided with the distribution.
13// 3. Neither the name of the University of Padova (SIGNET lab) nor the
14// names of its contributors may be used to endorse or promote products
15// derived from this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include <clmessage.h>
30#include <hdr-uwal.h>
31#include <phymac-clmsg.h>
32#include <uwal.h>
33#include <uwmodemcsa.h>
34#include <uwphy-clmsg.h>
35#include <uwsocket.h>
36
37#include <algorithm>
38#include <cstring>
39#include <iostream>
40#include <iterator>
41
42const std::chrono::milliseconds UwModemCSA::MODEM_TIMEOUT =
43 std::chrono::milliseconds(210);
44
45const double UwModemCSA::EPSILON_S = 0.01; // milliseconds
46
47const size_t UwModemCSA::MAX_TX_STATUS_POLL = 20;
48
53static class UwModemCSA_TclClass : public TclClass
54{
55
56public:
58 : TclClass("Module/UW/UwModem/ModemCSA")
59 {
60 }
61
62 TclObject *
63 create(int args, const char *const *argv)
64 {
65 return (new UwModemCSA());
66 }
67
69
71 : UwModem()
72 , p_connector(new UwSocket())
73 , status(ModemState::AVAILABLE)
74 , status_m()
75 , tx_queue_m()
76 , status_cv()
77 , tx_queue_cv()
78 , receiving(false)
79 , transmitting(false)
80 , rx_thread()
81 , tx_thread()
82 , rx_payload("")
83 , del_b("PACKET")
84 , del_e("EPCK")
85
86{
87 bind("buffer_size", (int *) &DATA_BUFFER_LEN);
88 bind("max_read_size", (int *) &MAX_READ_BYTES);
89}
90
95
96void
98{
99 // push new incoming pck into tx_queue
100 hdr_cmn *ch = HDR_CMN(p);
101 hdr_MPhy *ph = HDR_MPHY(p);
102 if (ch->direction() == hdr_cmn::UP) {
103 if (isOn == true) {
104 startRx(p);
105 endRx(p);
106
107 printOnLog(LogLevel::DEBUG, "MODEMCSA", "recv::PACKET_SENT_UP");
108
109 } else {
110 Packet::free(p);
111
112 printOnLog(LogLevel::DEBUG, "MODEMCSA", "recv::PACKET_DROPPED");
113 }
114 } else { // DOWN
115 if (!isOn) {
116 return;
117 }
118 ph->Pr = 0;
119 ph->Pn = 0;
120 ph->Pi = 0;
121 ph->txtime = NOW;
122 ph->rxtime = ph->txtime;
123
124 ph->worth_tracing = false;
125
126 ph->srcSpectralMask = getTxSpectralMask(p);
127 ph->srcAntenna = getTxAntenna(p);
128 ph->srcPosition = getPosition();
129 ph->dstSpectralMask = 0;
130 ph->dstPosition = 0;
131 ph->dstAntenna = 0;
132 ph->modulationType = getModulationType(p);
133 ph->duration = getTxDuration(p);
134
135 std::unique_lock<std::mutex> tx_lock(tx_queue_m);
136
137 tx_queue.push(p);
138 tx_lock.unlock();
139
140 printOnLog(LogLevel::DEBUG, "MODEMCSA", "recv::PUSHING_IN_TX_QUEUE");
141 tx_queue_cv.notify_one();
142 }
143}
144
145int
146UwModemCSA::command(int argc, const char *const *argv)
147{
148 if (argc == 2) {
149 if (!strcmp(argv[1], "setServer")) {
150 p_connector->setServer();
151 return TCL_OK;
152 }
153
154 if (!strcmp(argv[1], "setTCP")) {
155 p_connector->setTCP();
156 return TCL_OK;
157 }
158
159 if (!strcmp(argv[1], "setUDP")) {
160 p_connector->setUDP();
161 return TCL_OK;
162 }
163 }
164 return UwModem::command(argc, argv);
165}
166
167int
169{
170 if (m->type() == CLMSG_MAC2PHY_GETTXDURATION) {
171 Packet *p = ((ClMsgMac2PhyGetTxDuration *) m)->pkt;
172 hdr_cmn *ch = HDR_CMN(p);
173
174 if (ch->direction() == hdr_cmn::DOWN) {
175 double duration = getTxDuration(p);
176
177 if (duration > 0)
179 "MODEMCSA",
180 "recvSyncClMsg::GET_TXDURATION " +
181 std::to_string(duration));
182
183 ((ClMsgMac2PhyGetTxDuration *) m)->setDuration(duration);
184
185 return 0;
186 }
187
188 ((ClMsgMac2PhyGetTxDuration *) m)->setDuration(-1);
189
190 return 1;
191 }
192 return MPhy::recvSyncClMsg(m);
193}
194
195double
197{
198 // Difference between RX and TX time of a packet from localhost to localhost
199 double tx_duration = 0.002404;
200
201 return tx_duration;
202}
203
204void
206{
207 // this method does not do nothing in ns, so it can be called from an
208 // extrenal thread
209 // save MAC and AL headers and write payload
210 hdr_mac *mach = HDR_MAC(p);
211 hdr_uwal *uwalh = HDR_UWAL(p);
212 std::string payload;
213 payload.assign(uwalh->binPkt(), uwalh->binPktLength());
214
215 // build command to perform a SEND
216 std::string cmd_s;
217 cmd_s = buildSend(payload, mach->macDA());
218
219 printOnLog(LogLevel::INFO, "MODEMCSA", "startTx::COMMAND_TX::" + cmd_s);
220
221 // write the obtained command to the device, through the connector
222 std::unique_lock<std::mutex> state_lock(status_m);
223 if (status_cv.wait_for(state_lock, MODEM_TIMEOUT, [&] {
224 return status == ModemState::AVAILABLE;
225 })) {
226
228
229 if ((p_connector->writeToDevice(cmd_s)) < 0) {
231 "MODEMCSA",
232 "startTx::FAIL_TO_WRITE_TO_DEVICE=" + cmd_s);
233 return;
234 }
235
237 state_lock.unlock();
238 std::function<void(UwModem &, Packet * p)> callback =
240 ModemEvent e = {callback, p};
241 event_q.push(e);
242 }
243
244 return;
245}
246
247std::string
248UwModemCSA::buildSend(const std::string &payload, int dest)
249{
250 std::string cmd_s("");
251 cmd_s.append("PACKET,");
252 cmd_s += std::to_string(payload.size());
253 cmd_s.append(",");
254 cmd_s += payload;
255 cmd_s.append(",EPCK");
256
257 return cmd_s;
258}
259
260void
262{
263 printOnLog(LogLevel::INFO, "MODEMCSA", "startRx::CALL_PHY2MACSTARTRX");
264 Phy2MacStartRx(p);
265 return;
266}
267
268void
270{
271 printOnLog(LogLevel::INFO, "MODEMCSA", "endRx::CALL_SENDUP");
272 sendUp(p, 0.01);
273 return;
274}
275
276void
278{
279 if (modem_address == "") {
280 std::cout << "ERROR: Modem address not set!" << std::endl;
281 printOnLog(LogLevel::ERROR, "MODEMCSA", "start::ADDRESS_NOT_SET");
282 return;
283 }
284
285 if (!p_connector->openConnection(modem_address)) {
286 std::cout << "ERROR: connection to modem failed to open: "
287 << modem_address << std::endl;
289 LogLevel::ERROR, "MODEMCSA", "start::CONNECTION_OPEN_FAILED");
290 return;
291 }
292
293 // set flags to true so loops can start
294 receiving.store(true);
295 transmitting.store(true);
296
297 // branch off threads
298 rx_thread = std::thread(&UwModemCSA::receivingData, this);
299
300 tx_thread = std::thread(&UwModemCSA::transmittingData, this);
301
302 checkTimer = new CheckTimer(this);
303 checkTimer->resched(period);
304}
305
306void
308{
309 std::cout << "UwModemCSA::stop() close conn " << std::endl;
310 printOnLog(LogLevel::INFO, "MODEMCSA", "stop::CLOSING_CONNECTION");
311
312 receiving.store(false);
313 transmitting.store(false);
314 tx_queue_cv.notify_one();
315 if (tx_thread.joinable())
316 tx_thread.join();
317 if (p_connector->isConnected() && !p_connector->closeConnection()) {
318 printOnLog(LogLevel::ERROR, "MODEMCSA", "stop::CONNECTION_CLOSE_FAIL");
319 }
320 if (rx_thread.joinable()) {
321 rx_thread.join();
322 }
323
324 checkTimer->force_cancel();
325}
326
327void
329{
330 while (transmitting.load()) {
331
332 std::unique_lock<std::mutex> tx_lock(tx_queue_m);
333 tx_queue_cv.wait(
334 tx_lock, [&] { return !tx_queue.empty() || !transmitting; });
335 if (!transmitting) {
336 break;
337 }
338
339 Packet *pck = tx_queue.front();
340 tx_queue.pop();
341 tx_lock.unlock();
342 if (pck) {
343 startTx(pck);
344 }
345
347 "MODEMCSA",
348 "transmittingData::BLOCKING_ON_NEXT_PACKET");
349 }
350}
351
352void
354{
356 std::fill(data_buffer.begin(), data_buffer.end(), '\0');
357 // iterators that keep track of read/write operations
358 std::vector<char>::iterator beg_it = data_buffer.begin();
359 std::vector<char>::iterator end_it = data_buffer.begin();
360 // iterators that keep track of commands research
361 std::vector<char>::iterator cmd_b = data_buffer.begin();
362 std::vector<char>::iterator cmd_e = data_buffer.begin();
363 std::string cmd("");
364 int r_bytes = 0;
365 int offset = 0;
366
367 while (receiving.load()) {
368
369 r_bytes = p_connector->readFromDevice(
370 &(*beg_it) + offset, MAX_READ_BYTES - r_bytes);
371
372 end_it += r_bytes;
373
374 if (receiving.load() && r_bytes > 0 &&
375 (cmd = findCommand(beg_it, end_it, cmd_b, cmd_e)) != "") {
376
377 offset = std::distance(cmd_e, end_it);
378 if (parseCommand(cmd_b, cmd_e, rx_payload)) {
379
380 startRealRx(cmd);
381 }
382
383 offset = std::distance(cmd_e, end_it);
384 std::copy(cmd_e, end_it, beg_it);
385 std::fill(beg_it + offset, end_it, '\0');
386 end_it = beg_it + offset;
387
388 cmd = "";
389 }
390 }
391}
392
393std::string
394UwModemCSA::findCommand(std::vector<char>::iterator beg_it,
395 std::vector<char>::iterator end_it, std::vector<char>::iterator &cmd_b,
396 std::vector<char>::iterator &cmd_e)
397{
398 std::string cmd = "";
399 cmd_b = beg_it;
400 cmd_e = beg_it;
401
402 if (cmd_b == end_it)
403 return "";
404
405 cmd_b = std::search(beg_it, end_it, del_b.begin(), del_b.end());
406
407 if ((cmd_e = std::search(cmd_b, end_it, del_e.begin(), del_e.end())) !=
408 end_it) {
409 cmd_e += del_e.size();
410 return std::string(cmd_b, cmd_e);
411 }
412
413 return "";
414}
415
416bool
417UwModemCSA::parseCommand(std::vector<char>::iterator cmd_b,
418 std::vector<char>::iterator cmd_e, std::string &rx_payload)
419{
420 // check only packet correctness
421 auto curs_b = find(cmd_b, cmd_e, ',');
422 auto curs_e = find(cmd_b, cmd_e, ',') + 1;
423
424 if (std::string(cmd_b, curs_b).compare("PACKET") != 0)
425 return false;
426 curs_b = curs_e;
427
428 // length
429 curs_e = find(curs_b, cmd_e, ',');
430
431 if (curs_e >= cmd_e)
432 return false;
433 int len = std::stoi(std::string(curs_b, curs_e));
434 curs_b = curs_e + 1;
435 if (curs_b >= cmd_e)
436 return false;
437
438 // payload
439 auto payload_beg = curs_b;
440 if (payload_beg >= cmd_e)
441 return false;
442 auto payload_end = payload_beg + len;
443 rx_payload = std::string(payload_beg, payload_end);
444
445 return true;
446}
447
448void
449UwModemCSA::startRealRx(const std::string &cmd)
450{
451 std::unique_lock<std::mutex> state_lock(status_m);
452
454 status_cv.notify_all();
455 state_lock.unlock();
456
457 Packet *p = Packet::alloc();
459 std::function<void(UwModem &, Packet * p)> callback = &UwModem::recv;
460 ModemEvent e = {callback, p};
461 event_q.push(e);
462 // recv(p);
463}
464
465void
467{
468 hdr_uwal *uwalh = HDR_UWAL(p);
469 uwalh->binPktLength() = rx_payload.size();
470 std::memset(uwalh->binPkt(), 0, uwalh->binPktLength());
471 std::copy(rx_payload.begin(), rx_payload.end(), uwalh->binPkt());
472 HDR_CMN(p)->direction() = hdr_cmn::UP;
473}
Class to create the Otcl shadow object for an object of the class UwModemCSA.
TclObject * create(int args, const char *const *argv)
virtual void endRx(Packet *p)
Method that ends a packet reception.
std::string rx_payload
String that is updated witn each new received messsage.
Definition uwmodemcsa.h:201
virtual void startRx(Packet *p)
Method that starts a packet reception.
ModemState status
Variable holding the current status of the modem.
Definition uwmodemcsa.h:182
virtual bool parseCommand(std::vector< char >::iterator cmd_b, std::vector< char >::iterator cmd_e, std::string &rx_payload)
Method that parses the command to obtain the recquired informations.
virtual std::string findCommand(std::vector< char >::iterator beg_it, std::vector< char >::iterator end_it, std::vector< char >::iterator &cmd_b, std::vector< char >::iterator &cmd_e)
Method that finds the position of a command in a buffer.
static const size_t MAX_TX_STATUS_POLL
maximum number of time to poll the modem transmission status before to discard the transmitted packet
Definition uwmodemcsa.h:213
virtual void recv(Packet *p)
Method that handles the reception of packets arriving from upper layers of the network simulator.
std::condition_variable status_cv
Condition variable to wait for ModemState::AVAILABLE.
Definition uwmodemcsa.h:189
virtual int command(int argc, const char *const *argv)
Tcl command interpreter: Method that maps Tcl commands into C++ methods.
virtual void receivingData()
Method that detach a thread devoted to receiving data from the connector.
std::atomic< bool > transmitting
Atomic boolean variable that controls the transmitting looping thread.
Definition uwmodemcsa.h:195
virtual void start()
Method that starts the driver operations.
virtual void stop()
Method that stops the driver operations.
void startRealRx(const std::string &cmd)
Method that updates the status of the modem State Machine: state change is triggered by reception of ...
virtual std::string buildSend(const std::string &payload, int dest)
static const std::chrono::milliseconds MODEM_TIMEOUT
Maximum time to wait for modem to become ModemState::AVAILABLE.
Definition uwmodemcsa.h:207
virtual int recvSyncClMsg(ClMessage *m)
Cross-Layer messages synchronous interpreter.
std::condition_variable tx_queue_cv
Condition variable that is linked with the transmitting queue.
Definition uwmodemcsa.h:191
virtual double getTxDuration(Packet *p)
Method that returns the duration of a given transmitted packet.
std::mutex tx_queue_m
Mutex associated with the transmission queue.
Definition uwmodemcsa.h:187
std::atomic< bool > receiving
Atomic boolean variable that controls the receiving looping thread.
Definition uwmodemcsa.h:193
virtual void transmittingData()
Method that detach a thread devoted to sending packets found in tx_queue.
void createRxPacket(Packet *p)
std::thread tx_thread
Object with the tx thread.
Definition uwmodemcsa.h:199
std::thread rx_thread
Object with the rx thread.
Definition uwmodemcsa.h:197
std::unique_ptr< UwConnector > p_connector
Pointer to Connector object that interfaces with the device.
Definition uwmodemcsa.h:180
std::string del_e
Definition uwmodemcsa.h:205
virtual ~UwModemCSA()
Destructor of the UwModemCSA class.
virtual void startTx(Packet *p)
Method that triggers the transmission of a packet through a specified modem.
std::mutex status_m
Mutex associated with the state machine of the modem.
Definition uwmodemcsa.h:185
static const double EPSILON_S
minimum time to wait before to schedule a new event in seconds
Definition uwmodemcsa.h:210
std::string del_b
Definition uwmodemcsa.h:203
UwModemCSA()
Constructor of the UwModemCSA class.
Class that implements the interface to DESERT, as used through Tcl scripts.
Definition uwmodem.h:62
void realTxEnded(Packet *p)
Method to call endTx from end of real packet transmission.
Definition uwmodem.h:219
friend class CheckTimer
Definition uwmodem.h:63
std::string modem_address
String containing the address needed to connect to the device In case of socket, it may be expressed ...
Definition uwmodem.h:252
virtual void recv(Packet *p)=0
Method that handles the reception of packets arriving from the upper layers of the network simulator.
std::queue< ModemEvent > event_q
Queue of events that are scheduled for NS2 to execute (callbacks)
Definition uwmodem.h:269
int MAX_READ_BYTES
Maximum number of bytes to be read by a single dump of data.
Definition uwmodem.h:245
CheckTimer * checkTimer
Pointer to an object to schedule the "check-modem" events.
Definition uwmodem.h:265
std::queue< Packet * > tx_queue
Modem's transmission queue: holds packets that are to be transmitted.
Definition uwmodem.h:234
void printOnLog(LogLevel log_level, string module, string message)
Function that, given the appropriate level of log, prints to the set log file the provided log messag...
Definition uwmodem.cpp:102
unsigned int DATA_BUFFER_LEN
Size of the buffer that holds data.
Definition uwmodem.h:242
virtual int getModulationType(Packet *P)
Method that should return the modulation type used for the packet being transmitted.
Definition uwmodem.h:152
virtual int command(int argc, const char *const *argv)
Tcl command interpreter: Method that maps Tcl commands into C++ methods.
Definition uwmodem.cpp:127
std::vector< char > data_buffer
Char buffer (vector) that holds data read from the modem (unparsed data) Main container for data rece...
Definition uwmodem.h:231
double period
Checking period of the modem's buffer.
Definition uwmodem.h:267
Class that implements a TCP or UDP socket.
Definition uwsocket.h:59
Header of the class providing the Uwal header's description.
#define HDR_UWAL(p)
Definition hdr-uwal.h:43
hdr_uwal describes the packet header used by Uwal objects.
Definition hdr-uwal.h:52
char * binPkt()
Return to the binPkt_ array pointer.
Definition hdr-uwal.h:141
uint32_t & binPktLength()
Reference to the binPktLength_ variable.
Definition hdr-uwal.h:150
Header of the main class that implements the adaptation layer between ns2/NS-Miracle and binary data ...
UwModemCSA_TclClass class_modemcsa
Class that implements a generic modem driver for end-to-end communications.
ModemState
Definition uwmodemcsa.h:50
Definition of ClMsgUwMmac class.
Class that implements a connector and, specifically, the socket connector. BSD sockets are used,...