Add simple UDP socket wrapper classes

Change-Id: Iad836899a1c91c9a5a7e95ff3cf2d2687a1bbdf8
diff --git a/aos/vision/events/BUILD b/aos/vision/events/BUILD
new file mode 100644
index 0000000..e5be1b0
--- /dev/null
+++ b/aos/vision/events/BUILD
@@ -0,0 +1,19 @@
+cc_library(
+  name = 'udp',
+  srcs = ['udp.cc'],
+  hdrs = ['udp.h'],
+  deps = [
+    '//aos/common:macros',
+    '//aos/common/logging',
+    '//aos/common:scoped_fd',
+  ],
+)
+
+cc_test(
+  name = 'udp_test',
+  srcs = ['udp_test.cc'],
+  deps = [
+    ':udp',
+    '//aos/testing:googletest'
+  ],
+)
diff --git a/aos/vision/events/udp.cc b/aos/vision/events/udp.cc
new file mode 100644
index 0000000..febb835
--- /dev/null
+++ b/aos/vision/events/udp.cc
@@ -0,0 +1,44 @@
+#include "aos/vision/events/udp.h"
+
+#include <string.h>
+
+#include "aos/common/logging/logging.h"
+
+namespace aos {
+namespace vision {
+
+TXUdpSocket::TXUdpSocket(const char *ip_addr, int port)
+    : fd_(PCHECK(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))) {
+  sockaddr_in destination_in{};
+  destination_in.sin_family = AF_INET;
+  destination_in.sin_port = htons(port);
+  if (inet_aton(ip_addr, &destination_in.sin_addr) == 0) {
+    LOG(FATAL, "invalid IP address %s\n", ip_addr);
+  }
+
+  PCHECK(connect(fd_.get(), reinterpret_cast<sockaddr *>(&destination_in),
+                 sizeof(destination_in)));
+}
+
+int TXUdpSocket::Send(const void *data, int size) {
+  return PCHECK(send(fd_.get(), static_cast<const char *>(data), size, 0));
+}
+
+RXUdpSocket::RXUdpSocket(int port)
+    : fd_(PCHECK(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))) {
+  sockaddr_in bind_address{};
+
+  bind_address.sin_family = AF_INET;
+  bind_address.sin_port = htons(port);
+  bind_address.sin_addr.s_addr = htonl(INADDR_ANY);
+
+  PCHECK(bind(fd_.get(), reinterpret_cast<sockaddr *>(&bind_address),
+              sizeof(bind_address)));
+}
+
+int RXUdpSocket::Recv(void *data, int size) {
+  return PCHECK(recv(fd_.get(), static_cast<char *>(data), size, 0));
+}
+
+}  // namespace vision
+}  // namespace aos
diff --git a/aos/vision/events/udp.h b/aos/vision/events/udp.h
new file mode 100644
index 0000000..a12a8bf
--- /dev/null
+++ b/aos/vision/events/udp.h
@@ -0,0 +1,51 @@
+#ifndef AOS_VISION_IMAGE_UDP_H_
+#define AOS_VISION_IMAGE_UDP_H_
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <math.h>
+#include <unistd.h>
+#include <vector>
+
+#include "aos/common/macros.h"
+#include "aos/common/scoped_fd.h"
+
+namespace aos {
+namespace vision {
+
+// Simple wrapper around a transmitting UDP socket.
+//
+// LOG(FATAL)s for all errors, including from Send.
+class TXUdpSocket {
+ public:
+  TXUdpSocket(const char *ip_addr, int port);
+
+  // Returns the number of bytes actually sent.
+  int Send(const void *data, int size);
+
+ private:
+  ScopedFD fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(TXUdpSocket);
+};
+
+// Simple wrapper around a receiving UDP socket.
+//
+// LOG(FATAL)s for all errors, including from Recv.
+class RXUdpSocket {
+ public:
+  RXUdpSocket(int port);
+
+  // Returns the number of bytes received.
+  int Recv(void *data, int size);
+
+ private:
+  ScopedFD fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(RXUdpSocket);
+};
+
+}  // namespace vision
+}  // namespace aos
+
+#endif  // AOS_VISION_IMAGE_UDP_H_
diff --git a/aos/vision/events/udp_test.cc b/aos/vision/events/udp_test.cc
new file mode 100644
index 0000000..87046ba
--- /dev/null
+++ b/aos/vision/events/udp_test.cc
@@ -0,0 +1,23 @@
+#include "aos/vision/events/udp.h"
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace vision {
+
+TEST(UDPTest, SendRecv) {
+  RXUdpSocket rx(1109);
+  TXUdpSocket tx("127.0.0.01", 1109);
+
+  int txdata[] = {1, 2, 3, 4};
+  int rxdata[4];
+  tx.Send(static_cast<const void *>(&txdata), sizeof(txdata));
+  rx.Recv(static_cast<void *>(&rxdata), sizeof(rxdata));
+  EXPECT_EQ(txdata[0], rxdata[0]);
+  EXPECT_EQ(txdata[1], rxdata[1]);
+  EXPECT_EQ(txdata[2], rxdata[2]);
+  EXPECT_EQ(txdata[3], rxdata[3]);
+}
+
+}  // namespace vision
+}  // namespace aos