LCOV - code coverage report
Current view: top level - wibmod/src/WIB1 - BNL_UDP.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 243 0
Test Date: 2026-10-11 15:05:20 Functions: 0.0 % 12 0

            Line data    Source code
       1              : #include "wibmod/WIB1/BNL_UDP.hh"
       2              : #include "wibmod/WIB1/BNL_UDP_Exception.hh"
       3              : #include <errno.h>
       4              : #include <fcntl.h>  //fcntl
       5              : #include <string.h> //memset, strerror
       6              : #include <string>
       7              : #include <sys/socket.h>
       8              : 
       9              : #include <iomanip>
      10              : #include <sstream>
      11              : 
      12              : #include <netdb.h>
      13              : 
      14              : #include <algorithm> //STD::COUNT
      15              : #include <errno.h>
      16              : 
      17              : #define WIB_WR_BASE_PORT 32000
      18              : #define WIB_RD_BASE_PORT 32001
      19              : #define WIB_RPLY_BASE_PORT 32002
      20              : 
      21              : #define WIB_PACKET_KEY 0xDEADBEEF
      22              : // #define WIB_REQUEST_PACKET_SIZE (4+2+4+2)
      23              : #define WIB_REQUEST_PACKET_TRAILER 0xFFFF
      24              : 
      25              : #define WIB_RPLY_PACKET_SIZE 12
      26              : 
      27              : #define TIMEOUT_SECONDS 2
      28              : #define TIMEOUT_MICROSECONDS 0
      29              : struct WIB_packet_t
      30              : {
      31              :   uint32_t key;
      32              :   uint32_t reg_addr : 16;
      33              :   uint32_t data_MSW : 16;
      34              :   uint32_t data_LSW : 16;
      35              :   uint32_t trailer : 16;
      36              : };
      37              : 
      38              : static std::string
      39            0 : dump_packet(uint8_t* data, size_t size)
      40              : {
      41              :   //  printf("Err: %p %zu\n",data,size);
      42            0 :   std::stringstream ss;
      43            0 :   for (size_t iWord = 0; iWord < size; iWord++) {
      44            0 :     ss << "0x" << std::hex << std::setfill('0') << std::setw(4) << iWord;
      45            0 :     ss << ": 0x" << std::hex << std::setfill('0') << std::setw(2) << int(data[iWord]);
      46            0 :     iWord++;
      47            0 :     if (iWord < size) {
      48            0 :       ss << std::hex << std::setfill('0') << std::setw(2) << int(data[iWord]);
      49              :     }
      50            0 :     ss << std::endl;
      51              :   }
      52              :   //  printf("%s",ss.str().c_str());
      53            0 :   return ss.str();
      54            0 : }
      55              : 
      56              : void
      57            0 : BNL_UDP::FlushSocket(int sock)
      58              : {
      59              :   // turn on non-blocking
      60            0 :   fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
      61            0 :   int ret;
      62            0 :   do {
      63            0 :     ret = recv(sock, buffer, buffer_size, 0);
      64            0 :   } while (ret != -1);
      65            0 :   fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) & (~O_NONBLOCK));
      66            0 : }
      67              : 
      68              : void
      69            0 : BNL_UDP::Clear()
      70              : {
      71              :   // close sockets
      72            0 :   if (readSocketFD != -1) {
      73            0 :     close(readSocketFD);
      74            0 :     readSocketFD = -1;
      75              :   }
      76            0 :   if (writeSocketFD != -1) {
      77            0 :     close(writeSocketFD);
      78            0 :     writeSocketFD = -1;
      79              :   }
      80              :   // Clear packet buffer
      81            0 :   if (buffer != NULL) {
      82            0 :     delete[] buffer;
      83            0 :     buffer_size = 0;
      84              :   }
      85            0 :   writeAck = false;
      86            0 :   connected = false;
      87              : 
      88              :   // Reset send/recv addr structures
      89            0 :   memset(&readAddr, 0, sizeof(readAddr));
      90            0 :   memset(&writeAddr, 0, sizeof(writeAddr));
      91            0 : }
      92              : 
      93              : // static void printaddress(struct sockaddr_in const * addr){
      94              : //   printf("%u: %u.%u.%u.%u : %u\n",
      95              : //       addr->sin_family,
      96              : //       (addr->sin_addr.s_addr >> 0)&0xFF,
      97              : //       (addr->sin_addr.s_addr >> 8)&0xFF,
      98              : //       (addr->sin_addr.s_addr >> 16)&0xFF,
      99              : //       (addr->sin_addr.s_addr >> 24)&0xFF,
     100              : //       ntohs(addr->sin_port));
     101              : // }
     102              : 
     103              : void
     104            0 : BNL_UDP::Setup(std::string const& address, uint16_t port_offset)
     105              : {
     106              :   // Reset the network structures
     107            0 :   Clear();
     108              : 
     109              :   // Allocate the recv buffer
     110            0 :   ResizeBuffer();
     111              : 
     112              :   // Check port_offset range
     113            0 :   if (port_offset > 128) {
     114            0 :     BUException::BNL_UDP_PORT_OUT_OF_RANGE e;
     115            0 :     throw e;
     116            0 :   }
     117              : 
     118              :   // Set the ports for this device (FEMBs are iFEMB*0x10 above the base)
     119            0 :   readPort = WIB_RD_BASE_PORT + port_offset;
     120            0 :   writePort = WIB_WR_BASE_PORT + port_offset;
     121              :   //  replyPort = WIB_RPLY_BASE_PORT + port_offset;
     122              : 
     123            0 :   remoteAddress = address;
     124              :   // Get the sockaddr for the address
     125            0 :   struct addrinfo* res;
     126            0 :   if (getaddrinfo(address.c_str(), NULL, NULL, &res)) {
     127              :     // Check if we have just one "." character and is less than 5 characters
     128            0 :     if (address.size() <= 5 && 1 == std::count(address.begin(), address.end(), '.')) {
     129            0 :       std::string strCrate = address.substr(0, address.find('.'));
     130            0 :       std::string strSlot = address.substr(address.find('.') + 1);
     131            0 :       if (strCrate.size() != 0 && strSlot.size() != 0) {
     132            0 :         uint8_t crate = strtoul(strCrate.c_str(), NULL, 0);
     133            0 :         uint8_t slot = strtoul(strSlot.c_str(), NULL, 0);
     134            0 :         if ((((crate > 0) && (crate < 7)) || 0xF == crate) && (((slot > 0) && (slot < 7)) || 0xF == slot)) {
     135            0 :           remoteAddress = std::string("192.168.");
     136              :           // Add the crate part of the address (200 + crate number)
     137            0 :           if (crate == 0xF) {
     138            0 :             remoteAddress += "200.";
     139              :           } else {
     140              :             // generate the crate number which is 200 + crate number
     141            0 :             remoteAddress += "20";
     142            0 :             remoteAddress += ('0' + crate);
     143            0 :             remoteAddress += '.';
     144              :           }
     145            0 :           if (slot == 0xF) {
     146            0 :             remoteAddress += "50";
     147              :           } else {
     148              :             // crate last IP octet that is slot number
     149            0 :             remoteAddress += ('0' + slot);
     150              :           }
     151              :         }
     152              :       }
     153            0 :     }
     154              :     // try a second time assumin gthis is a crate.slot address, fail if this still doesn't work
     155            0 :     if (getaddrinfo(address.c_str(), NULL, NULL, &res)) {
     156            0 :       BUException::BAD_REMOTE_IP e;
     157            0 :       e.Append("Addr: ");
     158            0 :       e.Append(address.c_str());
     159            0 :       e.Append(" could not be resolved.\n");
     160            0 :       throw e;
     161            0 :     }
     162              :   }
     163              :   //  readAddr = *((struct sockaddr_in *) res->ai_addr);
     164              :   //  printaddress(&readAddr);
     165              : 
     166              :   // Generate the sockets for read and write
     167            0 :   if ((readSocketFD = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
     168            0 :     BUException::BAD_SOCKET e;
     169            0 :     e.Append("read socket\n");
     170            0 :     throw e;
     171            0 :   }
     172            0 :   if ((writeSocketFD = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
     173            0 :     BUException::BAD_SOCKET e;
     174            0 :     e.Append("write socket\n");
     175            0 :     throw e;
     176            0 :   }
     177              :   // Set a timeout for the recv socket so we don't hang on a reply
     178            0 :   struct timeval tv;
     179            0 :   tv.tv_sec = TIMEOUT_SECONDS;
     180            0 :   tv.tv_usec = TIMEOUT_MICROSECONDS;
     181            0 :   setsockopt(readSocketFD, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
     182            0 :   setsockopt(writeSocketFD, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
     183              : 
     184              :   // connect the read socket
     185            0 :   readAddr = *((struct sockaddr_in*)res->ai_addr);
     186            0 :   readAddr.sin_port = htons(readPort);
     187            0 :   if (connect(readSocketFD, (struct sockaddr*)&readAddr, sizeof(readAddr)) < 0) {
     188            0 :     BUException::CONNECTION_FAILED e;
     189            0 :     e.Append("read socket connect\n");
     190            0 :     e.Append(strerror(errno));
     191            0 :     throw e;
     192            0 :   }
     193              :   // connect the write socket
     194            0 :   writeAddr = *((struct sockaddr_in*)res->ai_addr);
     195            0 :   writeAddr.sin_port = htons(writePort);
     196            0 :   if (connect(writeSocketFD, (struct sockaddr*)&writeAddr, sizeof(writeAddr)) < 0) {
     197            0 :     BUException::CONNECTION_FAILED e;
     198            0 :     e.Append("write socket connect\n");
     199            0 :     e.Append(strerror(errno));
     200            0 :     throw e;
     201            0 :   }
     202              : 
     203              :   // Allocate the receive buffer to default size
     204            0 :   ResizeBuffer();
     205            0 : }
     206              : 
     207              : void
     208            0 : BNL_UDP::WriteWithRetry(uint16_t address, uint32_t value, uint8_t retry_count)
     209              : {
     210            0 :   while (retry_count > 1) {
     211            0 :     try {
     212              :       // Do the write
     213            0 :       Write(address, value);
     214            0 :       usleep(10);
     215              :       // if everything goes well, return
     216              :       return;
     217            0 :     } catch (BUException::BAD_REPLY& e) {
     218              :       // eat the exception
     219            0 :     }
     220            0 :     total_retry_count++;
     221            0 :     retry_count--;
     222            0 :     usleep(10);
     223              :   }
     224              :   // Last chance we don't catch the exception and let it fall down the stack
     225              :   // Do the write
     226            0 :   Write(address, value);
     227            0 :   usleep(10);
     228              : }
     229              : void
     230            0 : BNL_UDP::Write(uint16_t address, uint32_t value)
     231              : {
     232              : 
     233              :   // Flush this socket
     234            0 :   FlushSocket(writeSocketFD);
     235              : 
     236              :   // Build the packet to send
     237              :   // build the send packet
     238            0 :   WIB_packet_t packet;
     239            0 :   packet.key = htonl(WIB_PACKET_KEY);
     240            0 :   packet.reg_addr = htons(address);
     241            0 :   packet.data_MSW = htons(uint16_t((value >> 16) & 0xFFFF));
     242            0 :   packet.data_LSW = htons(uint16_t((value >> 0) & 0xFFFF));
     243            0 :   packet.trailer = htons(WIB_REQUEST_PACKET_TRAILER);
     244              : 
     245              :   // send the packet
     246            0 :   ssize_t send_size = sizeof(packet);
     247            0 :   ssize_t sent_size = 0;
     248            0 :   if (send_size != (sent_size = send(writeSocketFD, &packet, send_size, 0))) {
     249              :     // bad send
     250            0 :     BUException::SEND_FAILED e;
     251            0 :     if (sent_size == -1) {
     252            0 :       e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
     253            0 :       e.Append("Errnum: ");
     254            0 :       e.Append(strerror(errno));
     255              :     }
     256            0 :     throw e;
     257            0 :   }
     258              : 
     259              :   // If configured, capture confirmation packet
     260            0 :   if (writeAck) {
     261            0 :     ssize_t reply_size = recv(writeSocketFD, buffer, buffer_size, 0);
     262              : 
     263            0 :     if (-1 == reply_size) {
     264            0 :       BUException::BAD_REPLY e;
     265            0 :       std::stringstream ss;
     266            0 :       e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
     267            0 :       ss << "Errnum(" << errno << "): " << strerror(errno) << "\n";
     268            0 :       e.Append(ss.str().c_str());
     269            0 :       e.Append(dump_packet((uint8_t*)&packet, send_size).c_str());
     270            0 :       throw e;
     271            0 :     } else if (reply_size < WIB_RPLY_PACKET_SIZE) {
     272            0 :       BUException::BAD_REPLY e;
     273            0 :       std::stringstream ss;
     274            0 :       ss << "Bad Size: " << reply_size << "\n";
     275            0 :       e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
     276            0 :       e.Append(ss.str().c_str());
     277            0 :       e.Append(dump_packet(buffer, reply_size).c_str());
     278            0 :       throw e;
     279            0 :     }
     280            0 :     uint16_t reply_address = uint16_t(buffer[0] << 8 | buffer[1]);
     281            0 :     if (reply_address != address) {
     282            0 :       BUException::BAD_REPLY e;
     283            0 :       std::stringstream ss;
     284            0 :       ss << "Bad address: " << uint32_t(address) << " != " << uint32_t(reply_address) << "\n";
     285            0 :       e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
     286            0 :       e.Append(ss.str().c_str());
     287            0 :       e.Append(dump_packet(buffer, reply_size).c_str());
     288            0 :       throw e;
     289            0 :     }
     290              :   }
     291            0 : }
     292              : void
     293            0 : BNL_UDP::Write(uint16_t address, std::vector<uint32_t> const& values)
     294              : {
     295            0 :   Write(address, values.data(), values.size());
     296            0 : }
     297              : void
     298            0 : BNL_UDP::Write(uint16_t address, uint32_t const* values, size_t word_count)
     299              : {
     300            0 :   for (size_t iWrite = 0; iWrite < word_count; iWrite++) {
     301            0 :     WriteWithRetry(address, values[iWrite]);
     302            0 :     address++;
     303              :   }
     304              : 
     305              :   ////////  //Flush the socket
     306              :   ////////  FlushSocket(writeSocketFD);
     307              :   ////////
     308              :   ////////  //Compute the size of this multi-write packet
     309              :   ////////  // values.size() - 1 gets the number of address and MSW/LSW groups that aren't already in the packet
     310              :   ////////  size_t packetSize = sizeof(WIB_packet_t) + (word_count-1)*6;
     311              :   ////////  //resize the buffer if needed
     312              :   ////////  ResizeBuffer(packetSize);
     313              :   ////////
     314              :   ////////  //Set the packet key
     315              :   ////////  (*((uint32_t*) buffer)) = htonl(WIB_PACKET_KEY);
     316              :   ////////  //Create a pointer to 16bit words that points to the parts of buffer that are after the first 32bit word
     317              :   ///(WIB key)
     318              :   ////////  uint16_t * packet = (uint16_t*) (buffer + sizeof(uint32_t));
     319              :   ////////  for(size_t iWord = 0; iWord < word_count;iWord++){
     320              :   ////////    //Set the word address
     321              :   ////////    packet[0] = htons(address);
     322              :   ////////    //Set the MS 16bit part of the 32bit word
     323              :   ////////    packet[1] = htons(uint16_t((values[iWord] >> 16) & 0xFFFF));
     324              :   ////////    //Set the LS 16bit part of the 32bit word
     325              :   ////////    packet[2] = htons(uint16_t((values[iWord] >>  0) & 0xFFFF));
     326              :   ////////    //move the write address forward 1 word
     327              :   ////////    address++;
     328              :   ////////    //move the packet pointer forward to the next word block
     329              :   ////////    packet+=3;
     330              :   ////////  }
     331              :   ////////  //Set the packet trailer
     332              :   ////////  (*packet) = htons(WIB_REQUEST_PACKET_TRAILER);
     333              :   ////////
     334              :   ////////  //send the packet
     335              :   ////////  ssize_t send_size = packetSize;
     336              :   ////////  ssize_t sent_size = 0;
     337              :   ////////  if( send_size != (sent_size = send(writeSocketFD,
     338              :   ////////                                   buffer,send_size,0))){
     339              :   ////////    //bad send
     340              :   ////////    BUException::SEND_FAILED e;
     341              :   ////////    if(sent_size == -1){
     342              :   ////////      e.Append("BNL_UDP::Write(uint16_t,uint32_t*,size_t)\n");
     343              :   ////////      e.Append("Errnum: ");
     344              :   ////////      e.Append(strerror(errno));
     345              :   ////////    }
     346              :   ////////    throw e;
     347              :   ////////  }
     348              :   ////////  //If configured, capture confirmation packet
     349              :   ////////  if(writeAck ){
     350              :   ////////    ssize_t reply_size = recv(writeSocketFD,
     351              :   ////////                            buffer,buffer_size,0);
     352              :   ////////
     353              :   ////////
     354              :   ////////    //    writeAddr = readAddr;
     355              :   ////////    //    writeAddr.sin_port = htons(replyPort);
     356              :   ////////
     357              :   ////////    //    sockaddr_len = sizeof(writeAddr);
     358              :   ////////    //    ssize_t reply_size = recvfrom(recvSocketFD,
     359              :   //////////    recvfrom(recvSocketFD,
     360              :   //////////         buffer,buffer_size,0,
     361              :   //////////         (struct sockaddr*)&writeAddr,&sockaddr_len);
     362              :   ////////    if(-1 == reply_size){
     363              :   ////////      BUException::BAD_REPLY e;
     364              :   ////////      e.Append("BNL_UDP::Write(uint16_t,uint32_t*,size_t)\n");
     365              :   ////////      e.Append(strerror(errno));
     366              :   ////////      throw e;
     367              :   ////////    }else if( reply_size < WIB_RPLY_PACKET_SIZE){
     368              :   ////////      BUException::BAD_REPLY e;
     369              :   ////////      std::stringstream ss;
     370              :   ////////      ss << "Bad Size: " << reply_size << "\n";
     371              :   ////////      e.Append("BNL_UDP::Write(uint16_t,uint32_t*,size_t)\n");
     372              :   ////////      e.Append(ss.str().c_str());
     373              :   ////////      e.Append(dump_packet(buffer,reply_size).c_str());
     374              :   ////////      throw e;
     375              :   ////////    }
     376              :   //////////    uint16_t reply_address =  uint16_t(buffer[0] << 8 | buffer[1]);
     377              :   //////////    if( reply_address != address){
     378              :   //////////      BUException::BAD_REPLY e;
     379              :   //////////      std::stringstream ss;
     380              :   //////////      ss << "Bad address: " << address << " != " << reply_address << "\n";
     381              :   //////////      e.Append(ss.str().c_str());
     382              :   //////////      throw e;
     383              :   //////////    }
     384              :   ////////  }
     385            0 : }
     386              : 
     387              : uint32_t
     388            0 : BNL_UDP::ReadWithRetry(uint16_t address, uint8_t retry_count)
     389              : {
     390            0 :   uint32_t val;
     391            0 :   while (retry_count > 1) {
     392            0 :     try {
     393              :       // Do the write
     394            0 :       val = Read(address);
     395            0 :       usleep(10);
     396              :       // if everything goes well, return
     397              :       return val;
     398            0 :     } catch (BUException::BAD_REPLY& e) {
     399              :       // eat the exception
     400            0 :     }
     401            0 :     usleep(10);
     402            0 :     total_retry_count++;
     403            0 :     retry_count--;
     404              :   }
     405              :   // Last chance we don't catch the exception and let it fall down the stack
     406            0 :   val = Read(address);
     407            0 :   usleep(10);
     408            0 :   return val;
     409              : }
     410              : uint32_t
     411            0 : BNL_UDP::Read(uint16_t address)
     412              : {
     413              :   // Flush the socket
     414            0 :   FlushSocket(readSocketFD);
     415              : 
     416              :   // build the send packet
     417            0 :   WIB_packet_t packet;
     418            0 :   packet.key = htonl(WIB_PACKET_KEY);
     419            0 :   packet.reg_addr = htons(address);
     420            0 :   packet.data_MSW = packet.data_LSW = 0;
     421            0 :   packet.trailer = htons(WIB_REQUEST_PACKET_TRAILER);
     422              : 
     423              :   // send the packet
     424            0 :   ssize_t send_size = sizeof(packet);
     425            0 :   ssize_t sent_size = 0;
     426            0 :   if (send_size != (sent_size = send(readSocketFD, &packet, send_size, 0))) {
     427              :     // bad send
     428            0 :     BUException::SEND_FAILED e;
     429            0 :     if (sent_size == -1) {
     430            0 :       e.Append("BNL_UDP::Read(uint16_t)\n");
     431            0 :       e.Append("Errnum: ");
     432            0 :       e.Append(strerror(errno));
     433              :     }
     434            0 :     throw e;
     435            0 :   }
     436              : 
     437              :   // Get the reply packet with the register data in it.
     438            0 :   ssize_t reply_size = recv(readSocketFD, buffer, buffer_size, 0);
     439              : 
     440            0 :   if (ssize_t(-1) == reply_size) {
     441            0 :     BUException::BAD_REPLY e;
     442            0 :     std::stringstream ss;
     443            0 :     e.Append("BNL_UDP::Read(uint16_t)\n");
     444            0 :     ss << "Errnum(" << errno << "): " << strerror(errno) << "\n";
     445            0 :     e.Append(ss.str().c_str());
     446            0 :     e.Append(dump_packet((uint8_t*)&packet, send_size).c_str());
     447            0 :     throw e;
     448            0 :   } else if (reply_size < WIB_RPLY_PACKET_SIZE) {
     449            0 :     BUException::BAD_REPLY e;
     450            0 :     std::stringstream ss;
     451            0 :     ss << "Bad Size: " << reply_size << "\n";
     452            0 :     e.Append("BNL_UDP::Read(uint16_t)\n");
     453            0 :     e.Append(ss.str().c_str());
     454            0 :     e.Append(dump_packet(buffer, reply_size).c_str());
     455            0 :     throw e;
     456            0 :   }
     457            0 :   uint16_t reply_address = uint16_t(buffer[0] << 8 | buffer[1]);
     458            0 :   if (reply_address != address) {
     459            0 :     BUException::BAD_REPLY e;
     460            0 :     std::stringstream ss;
     461            0 :     ss << "Bad address: " << uint32_t(address) << " != " << uint32_t(reply_address) << "\n";
     462            0 :     e.Append("BNL_UDP::Read(uint16_t)\n");
     463            0 :     e.Append(ss.str().c_str());
     464            0 :     e.Append(dump_packet(buffer, reply_size).c_str());
     465            0 :     throw e;
     466            0 :   }
     467              : 
     468              :   //  }
     469            0 :   uint32_t ret = ((uint32_t(buffer[2]) << 24) | (uint32_t(buffer[3]) << 16) | (uint32_t(buffer[4]) << 8) |
     470            0 :                   (uint32_t(buffer[5]) << 0));
     471            0 :   return ret;
     472              : }
     473              : 
     474            0 : BNL_UDP::~BNL_UDP()
     475              : {
     476            0 :   Clear();
     477            0 : }
     478              : 
     479              : void
     480            0 : BNL_UDP::ResizeBuffer(size_t size)
     481              : {
     482              :   // CHeck if the requested size is larger than the already allocated size
     483              :   //   printf("before %p %zu %zd\n",buffer,buffer_size,buffer_size);
     484            0 :   if (buffer_size < size) {
     485              :     // We need to re-allocate
     486            0 :     if (buffer != NULL) {
     487            0 :       delete[] buffer;
     488              :     }
     489            0 :     buffer = new uint8_t[size];
     490            0 :     buffer_size = size;
     491              :   }
     492              :   //  printf("after %p %zu %zd\n",buffer,buffer_size,buffer_size);
     493            0 : }
        

Generated by: LCOV version 2.0-1