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