artdaq_demo  v3_10_03
UDPReceiver_generator.cc
1 #define TRACE_NAME "UDPReceiver"
2 #include "artdaq/DAQdata/Globals.hh"
3 
4 #include "artdaq-demo/Generators/UDPReceiver.hh"
5 
6 #include "canvas/Utilities/Exception.h"
7 
8 #include "artdaq-core-demo/Overlays/UDPFragmentWriter.hh"
9 #include "artdaq-core/Utilities/SimpleLookupPolicy.hh"
10 #include "artdaq/Generators/GeneratorMacros.hh"
11 #include "cetlib_except/exception.h"
12 #include "fhiclcpp/ParameterSet.h"
13 #include "messagefacility/MessageLogger/MessageLogger.h"
14 
15 #include <sys/poll.h>
16 #include <fstream>
17 #include <iomanip>
18 #include <iostream>
19 #include <iterator>
20 
21 demo::UDPReceiver::UDPReceiver(fhicl::ParameterSet const &ps)
22  : CommandableFragmentGenerator(ps)
23  , dataport_(ps.get<int>("port", 6343))
24  , ip_(ps.get<std::string>("ip", "127.0.0.1"))
25  , expectedPacketNumber_(0)
26  , sendCommands_(ps.get<bool>("send_CAPTAN_commands", false))
27  , rawOutput_(ps.get<bool>("raw_output_enabled", false))
28  , rawPath_(ps.get<std::string>("raw_output_path", "/tmp"))
29 {
30  datasocket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
31  if (datasocket_ < 0)
32  {
33  throw art::Exception(art::errors::Configuration) << "UDPReceiver: Error creating socket!" << std::endl; // NOLINT(cert-err60-cpp)
34  exit(1);
35  }
36 
37  struct sockaddr_in si_me_data;
38  si_me_data.sin_family = AF_INET;
39  si_me_data.sin_port = htons(dataport_);
40  si_me_data.sin_addr.s_addr = htonl(INADDR_ANY);
41  if (bind(datasocket_, reinterpret_cast<struct sockaddr *>(&si_me_data), sizeof(si_me_data)) == -1) // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
42  {
43  throw art::Exception(art::errors::Configuration) // NOLINT(cert-err60-cpp)
44  << "UDPReceiver: Cannot bind data socket to port " << dataport_ << std::endl;
45  exit(1);
46  }
47 
48  si_data_.sin_family = AF_INET;
49  si_data_.sin_port = htons(dataport_);
50  if (inet_aton(ip_.c_str(), &si_data_.sin_addr) == 0)
51  {
52  throw art::Exception(art::errors::Configuration) // NOLINT(cert-err60-cpp)
53  << "UDPReceiver: Could not translate provided IP Address: " << ip_ << "\n";
54  exit(1);
55  }
56 }
57 
58 bool demo::UDPReceiver::getNext_(artdaq::FragmentPtrs &frags)
59 {
60  if (should_stop())
61  {
62  return false;
63  }
64 
65  demo::UDPFragment::Metadata metadata;
66  metadata.port = dataport_;
67  metadata.address = si_data_.sin_addr.s_addr;
68 
69  // And use it, along with the artdaq::Fragment header information
70  // (fragment id, sequence id, and user type) to create a fragment
71 
72  // We'll use the static factory function
73 
74  // artdaq::Fragment::FragmentBytes(std::size_t payload_size_in_bytes, sequence_id_t sequence_id,
75  // fragment_id_t fragment_id, type_t type, const T & metadata)
76 
77  // which will then return a unique_ptr to an artdaq::Fragment
78  // object. The advantage of this approach over using the
79  // artdaq::Fragment constructor is that, if we were to want to
80  // initialize the artdaq::Fragment with a nonzero-size payload (data
81  // after the artdaq::Fragment header and metadata), we could provide
82  // the size of the payload in bytes, rather than in units of the
83  // artdaq::Fragment's RawDataType (8 bytes, as of 3/26/14). The
84  // artdaq::Fragment constructor itself was not altered so as to
85  // maintain backward compatibility.
86 
87  std::size_t initial_payload_size = 0;
88 
89  frags.emplace_back(artdaq::Fragment::FragmentBytes(initial_payload_size, ev_counter(), fragment_id(),
90  artdaq::Fragment::FirstUserFragmentType, metadata));
91  // We now have a fragment to contain this event:
92  demo::UDPFragmentWriter thisFrag(*frags.back());
93 
94  bool haveData = false;
95  int16_t burst_end = -1;
96  uint8_t droppedPackets = 0;
97  while (!haveData)
98  {
99  if (should_stop())
100  {
101  return false;
102  }
103  struct pollfd ufds[1];
104  ufds[0].fd = datasocket_;
105  ufds[0].events = POLLIN | POLLPRI;
106 
107  int rv = poll(ufds, 1, 1000);
108  if (rv > 0)
109  {
110  // std::cout << "revents: " << ufds[0].revents << ", " << ufds[1].revents << std::endl;
111  if (ufds[0].revents == POLLIN || ufds[0].revents == POLLPRI)
112  {
113  uint8_t peekBuffer[2];
114  socklen_t addr_len = sizeof(si_data_);
115  recvfrom(datasocket_, peekBuffer, sizeof(peekBuffer), MSG_PEEK, reinterpret_cast<struct sockaddr *>(&si_data_), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
116  &addr_len);
117 
118  TLOG(TLVL_INFO) << "Recieved UDP Packet with sequence number " << std::hex << static_cast<int>(peekBuffer[1])
119  << "!";
120  // std::cout << "peekBuffer[1] == expectedPacketNumber_: " << std::hex << (int)peekBuffer[1] << " =?= "
121  // << (int)expectedPacketNumber_ << std::endl;
122 
123  uint8_t seqNum = peekBuffer[1];
124  ReturnCode dataCode = getReturnCode(peekBuffer[0]);
125  if (seqNum >= expectedPacketNumber_ || (seqNum < 10 && expectedPacketNumber_ > 200) ||
126  droppedPackets > 0 || expectedPacketNumber_ - seqNum > 20)
127  {
128  if (seqNum != expectedPacketNumber_ &&
129  (seqNum >= expectedPacketNumber_ || (seqNum < 10 && expectedPacketNumber_ > 200)))
130  {
131  int deltaHi = seqNum - expectedPacketNumber_;
132  int deltaLo = 255 + seqNum - expectedPacketNumber_;
133  droppedPackets += deltaLo < 255 ? deltaLo : deltaHi;
134  TLOG(TLVL_WARNING) << "Dropped/Delayed packets detected: " << droppedPackets << std::endl;
135  expectedPacketNumber_ = seqNum;
136  }
137  else if (seqNum != expectedPacketNumber_)
138  {
139  int delta = expectedPacketNumber_ - seqNum;
140  TLOG(TLVL_WARNING)
141  << "Sequence Number significantly different than expected! (delta: " << delta << ")";
142  }
143 
144  if (dataCode == ReturnCode::Read || dataCode == ReturnCode::First)
145  {
146  packetBuffers_.clear();
147  packetBuffer_t buffer;
148  memset(&buffer[0], 0, sizeof(packetBuffer_t));
149  socklen_t addr_len = sizeof(si_data_);
150  recvfrom(datasocket_, &buffer[0], sizeof(packetBuffer_t), 0, reinterpret_cast<struct sockaddr *>(&si_data_), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
151  &addr_len);
152  packetBuffers_.push_back(buffer);
153  TLOG(TLVL_DEBUG) << "Now placing UDP packet with sequence number " << std::hex << seqNum
154  << " into buffer.";
155  if (dataCode == ReturnCode::Read)
156  {
157  haveData = true;
158  }
159  else
160  {
161  droppedPackets = 0;
162  burst_end = -1;
163  }
164  }
165  else if ((dataCode == ReturnCode::Middle || dataCode == ReturnCode::Last) &&
166  !packetBuffers_.empty())
167  {
168  packetBuffer_t buffer;
169  memset(&buffer[0], 0, sizeof(packetBuffer_t));
170  socklen_t addr_len = sizeof(si_data_);
171  recvfrom(datasocket_, &buffer[0], sizeof(packetBuffer_t), 0, reinterpret_cast<struct sockaddr *>(&si_data_), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
172  &addr_len);
173  if (droppedPackets == 0)
174  {
175  packetBuffers_.push_back(buffer);
176  }
177  else if (burst_end == -1 || seqNum < burst_end)
178  {
179  bool found = false;
180  for (auto it = packetBuffers_.begin(); it != packetBuffers_.end();
181  ++it)
182  {
183  if (seqNum < (*it)[1])
184  {
185  packetBuffers_.insert(it, buffer);
186  droppedPackets--;
187  expectedPacketNumber_--;
188  }
189  }
190  if (!found)
191  {
192  packetBuffers_.push_back(buffer);
193  }
194  }
195  TLOG(TLVL_DEBUG) << "Now placing UDP packet with sequence number " << std::hex << seqNum
196  << " into buffer.";
197  if (dataCode == ReturnCode::Last && droppedPackets == 0)
198  {
199  while (getReturnCode(packetBuffers_.back()[0]) != ReturnCode::Last)
200  {
201  packetBuffers_.pop_back();
202  }
203  haveData = true;
204  }
205  else if (dataCode == ReturnCode::Last)
206  {
207  burst_end = seqNum;
208  }
209  else if (burst_end >= 0 && droppedPackets == 0)
210  {
211  while (getReturnCode(packetBuffers_.back()[0]) != ReturnCode::Last)
212  {
213  packetBuffers_.pop_back();
214  }
215  haveData = true;
216  }
217  }
218 
219  ++expectedPacketNumber_;
220  }
221  else
222  {
223  packetBuffer_t discardBuffer;
224  socklen_t addr_len = sizeof(si_data_);
225  recvfrom(datasocket_, &discardBuffer[0], sizeof(discardBuffer), 0,
226  reinterpret_cast<struct sockaddr *>(&si_data_), &addr_len); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
227  TLOG(TLVL_WARNING) << "Out-of-sequence packet detected and discarded!";
228  }
229  }
230  }
231  }
232 
233  packetBuffer_t &firstPacket = packetBuffers_.front();
234  TLOG(TLVL_DEBUG) << "Recieved data, now placing data with UDP sequence number " << static_cast<int>(firstPacket[1])
235  << " into UDPFragment";
236  thisFrag.resize(1500 * packetBuffers_.size() + 1);
237  std::ofstream output;
238  if (rawOutput_)
239  {
240  std::string outputPath = rawPath_ + "/UDPReceiver-" + ip_ + ":" + std::to_string(dataport_) + ".bin";
241  output.open(outputPath, std::ios::out | std::ios::app | std::ios::binary);
242  }
243 
244  DataType dataType = getDataType(firstPacket[0]);
245  thisFrag.set_hdr_type(static_cast<demo::UDPFragment::Header::data_type_t>(dataType));
246  int pos = 0;
247  for (auto jj : packetBuffers_)
248  {
249  for (int ii = 2; ii < 1500; ++ii)
250  {
251  // Null-terminate string types
252  if (jj.at(ii) == 0 && (dataType == DataType::JSON || dataType == DataType::String))
253  {
254  break;
255  }
256 
257  if (rawOutput_)
258  {
259  output.write(reinterpret_cast<const char *>(&(jj.at(ii))), sizeof(uint8_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
260  }
261  *(thisFrag.dataBegin() + pos) = jj.at(ii); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
262  ++pos;
263  }
264  }
265  if (dataType == DataType::JSON || dataType == DataType::String)
266  {
267  *(thisFrag.dataBegin() + pos) = 0; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
268  char zero = 0;
269  if (rawOutput_)
270  {
271  output.write(&zero, sizeof(char));
272  }
273  }
274  if (rawOutput_)
275  {
276  output.close();
277  }
278 
279  return true;
280 }
281 
282 void demo::UDPReceiver::start() { send(CommandType::Start_Burst); }
283 
284 void demo::UDPReceiver::stop() { send(CommandType::Stop_Burst); }
285 
286 void demo::UDPReceiver::pause() { send(CommandType::Stop_Burst); }
287 
288 void demo::UDPReceiver::resume() { send(CommandType::Start_Burst); }
289 
290 void demo::UDPReceiver::send(CommandType command)
291 {
292  if (sendCommands_)
293  {
294  CommandPacket packet;
295  packet.type = command;
296  packet.dataSize = 0;
297  sendto(datasocket_, &packet, sizeof(packet), 0, reinterpret_cast<struct sockaddr *>(&si_data_), sizeof(si_data_)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
298  }
299 }
300 
301 // The following macro is defined in artdaq's GeneratorMacros.hh header
302 DEFINE_ARTDAQ_COMMANDABLE_GENERATOR(demo::UDPReceiver)
ReturnCode
Enumeration describing status codes that indicate current sender position in the stream.
Definition: UDPReceiver.hh:46
An artdaq::CommandableFragmentGenerator which receives data in the form of UDP datagrams.
Definition: UDPReceiver.hh:81
CommandType
Enumeration describing valid command types.
Definition: UDPReceiver.hh:35
DataType
Enumeration describing potential data types.
Definition: UDPReceiver.hh:57
std::array< uint8_t, 1500 > packetBuffer_t
An array of 1500 bytes (MTU length)
Definition: UDPReceiver.hh:75
UDPReceiver(fhicl::ParameterSet const &ps)
UDPReceiver Constructor.