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