DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
BNL_UDP.cpp
Go to the documentation of this file.
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
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
38static std::string
39dump_packet(uint8_t* data, size_t size)
40{
41 // printf("Err: %p %zu\n",data,size);
42 std::stringstream ss;
43 for (size_t iWord = 0; iWord < size; iWord++) {
44 ss << "0x" << std::hex << std::setfill('0') << std::setw(4) << iWord;
45 ss << ": 0x" << std::hex << std::setfill('0') << std::setw(2) << int(data[iWord]);
46 iWord++;
47 if (iWord < size) {
48 ss << std::hex << std::setfill('0') << std::setw(2) << int(data[iWord]);
49 }
50 ss << std::endl;
51 }
52 // printf("%s",ss.str().c_str());
53 return ss.str();
54}
55
56void
58{
59 // turn on non-blocking
60 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
61 int ret;
62 do {
63 ret = recv(sock, buffer, buffer_size, 0);
64 } while (ret != -1);
65 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) & (~O_NONBLOCK));
66}
67
68void
70{
71 // close sockets
72 if (readSocketFD != -1) {
73 close(readSocketFD);
74 readSocketFD = -1;
75 }
76 if (writeSocketFD != -1) {
77 close(writeSocketFD);
78 writeSocketFD = -1;
79 }
80 // Clear packet buffer
81 if (buffer != NULL) {
82 delete[] buffer;
83 buffer_size = 0;
84 }
85 writeAck = false;
86 connected = false;
87
88 // Reset send/recv addr structures
89 memset(&readAddr, 0, sizeof(readAddr));
90 memset(&writeAddr, 0, sizeof(writeAddr));
91}
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
103void
104BNL_UDP::Setup(std::string const& address, uint16_t port_offset)
105{
106 // Reset the network structures
107 Clear();
108
109 // Allocate the recv buffer
110 ResizeBuffer();
111
112 // Check port_offset range
113 if (port_offset > 128) {
114 BUException::BNL_UDP_PORT_OUT_OF_RANGE e;
115 throw e;
116 }
117
118 // Set the ports for this device (FEMBs are iFEMB*0x10 above the base)
119 readPort = WIB_RD_BASE_PORT + port_offset;
120 writePort = WIB_WR_BASE_PORT + port_offset;
121 // replyPort = WIB_RPLY_BASE_PORT + port_offset;
122
123 remoteAddress = address;
124 // Get the sockaddr for the address
125 struct addrinfo* res;
126 if (getaddrinfo(address.c_str(), NULL, NULL, &res)) {
127 // Check if we have just one "." character and is less than 5 characters
128 if (address.size() <= 5 && 1 == std::count(address.begin(), address.end(), '.')) {
129 std::string strCrate = address.substr(0, address.find('.'));
130 std::string strSlot = address.substr(address.find('.') + 1);
131 if (strCrate.size() != 0 && strSlot.size() != 0) {
132 uint8_t crate = strtoul(strCrate.c_str(), NULL, 0);
133 uint8_t slot = strtoul(strSlot.c_str(), NULL, 0);
134 if ((((crate > 0) && (crate < 7)) || 0xF == crate) && (((slot > 0) && (slot < 7)) || 0xF == slot)) {
135 remoteAddress = std::string("192.168.");
136 // Add the crate part of the address (200 + crate number)
137 if (crate == 0xF) {
138 remoteAddress += "200.";
139 } else {
140 // generate the crate number which is 200 + crate number
141 remoteAddress += "20";
142 remoteAddress += ('0' + crate);
143 remoteAddress += '.';
144 }
145 if (slot == 0xF) {
146 remoteAddress += "50";
147 } else {
148 // crate last IP octet that is slot number
149 remoteAddress += ('0' + slot);
150 }
151 }
152 }
153 }
154 // try a second time assumin gthis is a crate.slot address, fail if this still doesn't work
155 if (getaddrinfo(address.c_str(), NULL, NULL, &res)) {
156 BUException::BAD_REMOTE_IP e;
157 e.Append("Addr: ");
158 e.Append(address.c_str());
159 e.Append(" could not be resolved.\n");
160 throw e;
161 }
162 }
163 // readAddr = *((struct sockaddr_in *) res->ai_addr);
164 // printaddress(&readAddr);
165
166 // Generate the sockets for read and write
167 if ((readSocketFD = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
168 BUException::BAD_SOCKET e;
169 e.Append("read socket\n");
170 throw e;
171 }
172 if ((writeSocketFD = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
173 BUException::BAD_SOCKET e;
174 e.Append("write socket\n");
175 throw e;
176 }
177 // Set a timeout for the recv socket so we don't hang on a reply
178 struct timeval tv;
179 tv.tv_sec = TIMEOUT_SECONDS;
180 tv.tv_usec = TIMEOUT_MICROSECONDS;
181 setsockopt(readSocketFD, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
182 setsockopt(writeSocketFD, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
183
184 // connect the read socket
185 readAddr = *((struct sockaddr_in*)res->ai_addr);
186 readAddr.sin_port = htons(readPort);
187 if (connect(readSocketFD, (struct sockaddr*)&readAddr, sizeof(readAddr)) < 0) {
188 BUException::CONNECTION_FAILED e;
189 e.Append("read socket connect\n");
190 e.Append(strerror(errno));
191 throw e;
192 }
193 // connect the write socket
194 writeAddr = *((struct sockaddr_in*)res->ai_addr);
195 writeAddr.sin_port = htons(writePort);
196 if (connect(writeSocketFD, (struct sockaddr*)&writeAddr, sizeof(writeAddr)) < 0) {
197 BUException::CONNECTION_FAILED e;
198 e.Append("write socket connect\n");
199 e.Append(strerror(errno));
200 throw e;
201 }
202
203 // Allocate the receive buffer to default size
204 ResizeBuffer();
205}
206
207void
208BNL_UDP::WriteWithRetry(uint16_t address, uint32_t value, uint8_t retry_count)
209{
210 while (retry_count > 1) {
211 try {
212 // Do the write
213 Write(address, value);
214 usleep(10);
215 // if everything goes well, return
216 return;
217 } catch (BUException::BAD_REPLY& e) {
218 // eat the exception
219 }
221 retry_count--;
222 usleep(10);
223 }
224 // Last chance we don't catch the exception and let it fall down the stack
225 // Do the write
226 Write(address, value);
227 usleep(10);
228}
229void
230BNL_UDP::Write(uint16_t address, uint32_t value)
231{
232
233 // Flush this socket
235
236 // Build the packet to send
237 // build the send packet
238 WIB_packet_t packet;
239 packet.key = htonl(WIB_PACKET_KEY);
240 packet.reg_addr = htons(address);
241 packet.data_MSW = htons(uint16_t((value >> 16) & 0xFFFF));
242 packet.data_LSW = htons(uint16_t((value >> 0) & 0xFFFF));
243 packet.trailer = htons(WIB_REQUEST_PACKET_TRAILER);
244
245 // send the packet
246 ssize_t send_size = sizeof(packet);
247 ssize_t sent_size = 0;
248 if (send_size != (sent_size = send(writeSocketFD, &packet, send_size, 0))) {
249 // bad send
250 BUException::SEND_FAILED e;
251 if (sent_size == -1) {
252 e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
253 e.Append("Errnum: ");
254 e.Append(strerror(errno));
255 }
256 throw e;
257 }
258
259 // If configured, capture confirmation packet
260 if (writeAck) {
261 ssize_t reply_size = recv(writeSocketFD, buffer, buffer_size, 0);
262
263 if (-1 == reply_size) {
264 BUException::BAD_REPLY e;
265 std::stringstream ss;
266 e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
267 ss << "Errnum(" << errno << "): " << strerror(errno) << "\n";
268 e.Append(ss.str().c_str());
269 e.Append(dump_packet((uint8_t*)&packet, send_size).c_str());
270 throw e;
271 } else if (reply_size < WIB_RPLY_PACKET_SIZE) {
272 BUException::BAD_REPLY e;
273 std::stringstream ss;
274 ss << "Bad Size: " << reply_size << "\n";
275 e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
276 e.Append(ss.str().c_str());
277 e.Append(dump_packet(buffer, reply_size).c_str());
278 throw e;
279 }
280 uint16_t reply_address = uint16_t(buffer[0] << 8 | buffer[1]);
281 if (reply_address != address) {
282 BUException::BAD_REPLY e;
283 std::stringstream ss;
284 ss << "Bad address: " << uint32_t(address) << " != " << uint32_t(reply_address) << "\n";
285 e.Append("BNL_UDP::Write(uint16_t,uint32_t)\n");
286 e.Append(ss.str().c_str());
287 e.Append(dump_packet(buffer, reply_size).c_str());
288 throw e;
289 }
290 }
291}
292void
293BNL_UDP::Write(uint16_t address, std::vector<uint32_t> const& values)
294{
295 Write(address, values.data(), values.size());
296}
297void
298BNL_UDP::Write(uint16_t address, uint32_t const* values, size_t word_count)
299{
300 for (size_t iWrite = 0; iWrite < word_count; iWrite++) {
301 WriteWithRetry(address, values[iWrite]);
302 address++;
303 }
304
385}
386
387uint32_t
388BNL_UDP::ReadWithRetry(uint16_t address, uint8_t retry_count)
389{
390 uint32_t val;
391 while (retry_count > 1) {
392 try {
393 // Do the write
394 val = Read(address);
395 usleep(10);
396 // if everything goes well, return
397 return val;
398 } catch (BUException::BAD_REPLY& e) {
399 // eat the exception
400 }
401 usleep(10);
403 retry_count--;
404 }
405 // Last chance we don't catch the exception and let it fall down the stack
406 val = Read(address);
407 usleep(10);
408 return val;
409}
410uint32_t
411BNL_UDP::Read(uint16_t address)
412{
413 // Flush the socket
415
416 // build the send packet
417 WIB_packet_t packet;
418 packet.key = htonl(WIB_PACKET_KEY);
419 packet.reg_addr = htons(address);
420 packet.data_MSW = packet.data_LSW = 0;
421 packet.trailer = htons(WIB_REQUEST_PACKET_TRAILER);
422
423 // send the packet
424 ssize_t send_size = sizeof(packet);
425 ssize_t sent_size = 0;
426 if (send_size != (sent_size = send(readSocketFD, &packet, send_size, 0))) {
427 // bad send
428 BUException::SEND_FAILED e;
429 if (sent_size == -1) {
430 e.Append("BNL_UDP::Read(uint16_t)\n");
431 e.Append("Errnum: ");
432 e.Append(strerror(errno));
433 }
434 throw e;
435 }
436
437 // Get the reply packet with the register data in it.
438 ssize_t reply_size = recv(readSocketFD, buffer, buffer_size, 0);
439
440 if (ssize_t(-1) == reply_size) {
441 BUException::BAD_REPLY e;
442 std::stringstream ss;
443 e.Append("BNL_UDP::Read(uint16_t)\n");
444 ss << "Errnum(" << errno << "): " << strerror(errno) << "\n";
445 e.Append(ss.str().c_str());
446 e.Append(dump_packet((uint8_t*)&packet, send_size).c_str());
447 throw e;
448 } else if (reply_size < WIB_RPLY_PACKET_SIZE) {
449 BUException::BAD_REPLY e;
450 std::stringstream ss;
451 ss << "Bad Size: " << reply_size << "\n";
452 e.Append("BNL_UDP::Read(uint16_t)\n");
453 e.Append(ss.str().c_str());
454 e.Append(dump_packet(buffer, reply_size).c_str());
455 throw e;
456 }
457 uint16_t reply_address = uint16_t(buffer[0] << 8 | buffer[1]);
458 if (reply_address != address) {
459 BUException::BAD_REPLY e;
460 std::stringstream ss;
461 ss << "Bad address: " << uint32_t(address) << " != " << uint32_t(reply_address) << "\n";
462 e.Append("BNL_UDP::Read(uint16_t)\n");
463 e.Append(ss.str().c_str());
464 e.Append(dump_packet(buffer, reply_size).c_str());
465 throw e;
466 }
467
468 // }
469 uint32_t ret = ((uint32_t(buffer[2]) << 24) | (uint32_t(buffer[3]) << 16) | (uint32_t(buffer[4]) << 8) |
470 (uint32_t(buffer[5]) << 0));
471 return ret;
472}
473
475{
476 Clear();
477}
478
479void
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 if (buffer_size < size) {
485 // We need to re-allocate
486 if (buffer != NULL) {
487 delete[] buffer;
488 }
489 buffer = new uint8_t[size];
490 buffer_size = size;
491 }
492 // printf("after %p %zu %zd\n",buffer,buffer_size,buffer_size);
493}
#define WIB_REQUEST_PACKET_TRAILER
Definition BNL_UDP.cpp:23
#define WIB_RD_BASE_PORT
Definition BNL_UDP.cpp:18
#define WIB_RPLY_PACKET_SIZE
Definition BNL_UDP.cpp:25
static std::string dump_packet(uint8_t *data, size_t size)
Definition BNL_UDP.cpp:39
#define WIB_WR_BASE_PORT
Definition BNL_UDP.cpp:17
#define TIMEOUT_MICROSECONDS
Definition BNL_UDP.cpp:28
#define WIB_PACKET_KEY
Definition BNL_UDP.cpp:21
#define TIMEOUT_SECONDS
Definition BNL_UDP.cpp:27
struct sockaddr_in writeAddr
Definition BNL_UDP.hh:61
int16_t readPort
Definition BNL_UDP.hh:52
void Setup(std::string const &address, uint16_t port_offset=0)
Definition BNL_UDP.cpp:104
std::string remoteAddress
Definition BNL_UDP.hh:51
void Write(uint16_t address, uint32_t value)
Definition BNL_UDP.cpp:230
bool connected
Definition BNL_UDP.hh:57
void WriteWithRetry(uint16_t address, uint32_t value, uint8_t retry_count=10)
Definition BNL_UDP.cpp:208
uint32_t ReadWithRetry(uint16_t address, uint8_t retry_count=10)
Definition BNL_UDP.cpp:388
int writeSocketFD
Definition BNL_UDP.hh:59
uint32_t Read(uint16_t address)
Definition BNL_UDP.cpp:411
void FlushSocket(int sock)
Definition BNL_UDP.cpp:57
uint64_t total_retry_count
Definition BNL_UDP.hh:66
void Clear()
Definition BNL_UDP.cpp:69
struct sockaddr_in readAddr
Definition BNL_UDP.hh:60
size_t buffer_size
Definition BNL_UDP.hh:64
int readSocketFD
Definition BNL_UDP.hh:58
void ResizeBuffer(size_t size=WIB_RESPONSE_PACKET_BUFFER_SIZE)
Definition BNL_UDP.cpp:480
uint8_t * buffer
Definition BNL_UDP.hh:65
int16_t writePort
Definition BNL_UDP.hh:53
bool writeAck
Definition BNL_UDP.hh:48
uint32_t key
Definition BNL_UDP.cpp:31
uint32_t data_LSW
Definition BNL_UDP.cpp:34
uint32_t reg_addr
Definition BNL_UDP.cpp:32
uint32_t data_MSW
Definition BNL_UDP.cpp:33
uint32_t trailer
Definition BNL_UDP.cpp:35