diff --git a/netutil.cpp b/netutil.cpp new file mode 100644 index 0000000..1cb8ac0 --- /dev/null +++ b/netutil.cpp @@ -0,0 +1,49 @@ +#include "netutil.h" + +/** + * \file netutil.cpp + * \author Alessandro Crespi + * \date April 2009 + * \brief Simple network utilities + */ + +uint32_t gethostaddress(const char* hn) +{ + struct hostent *hen; + uint32_t addr; + addr = inet_addr(hn); + if (addr != INADDR_ANY && addr != INADDR_NONE) { + return addr; + } + hen = gethostbyname(hn); + if (hen != NULL) { + if (sizeof(addr) != hen->h_length) { + fprintf(stderr, "Internal error, address length mismatch.\n"); + _exit(2); + } + memcpy(&addr, hen->h_addr_list[0], hen->h_length); + return addr; + } else { + return INADDR_NONE; + } +} + +// Receives a block of data of the specified size +int block_recv(const int sk, uint8_t* data, unsigned int len) +{ + int i, j; + + j = 0; + + while (len > 0) { + i = recv(sk, (char*) data, len, 0); + if (i==0 || i==-1) { + return -2; + } + data += i; + len -= i; + j += i; + } + + return j; +} diff --git a/netutil.h b/netutil.h new file mode 100644 index 0000000..f63cccd --- /dev/null +++ b/netutil.h @@ -0,0 +1,28 @@ +#ifndef __NETUTIL_H +#define __NETUTIL_H + +/** + * \file netutil.h + * \author Alessandro Crespi + * \date April 2009 + * \brief Simple network utilities header file + */ + +#include +#include +#include +#ifdef _WIN32 + #include +#else + #include + #include + #include + #include + #include +#endif + +uint32_t gethostaddress(const char* hn); +int block_recv(const int sk, uint8_t* data, unsigned int len); + +#endif + diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..994bb57 --- /dev/null +++ b/test.cpp @@ -0,0 +1,61 @@ +#include +#include + +/** + * \file test.cpp + * \author Alessandro Crespi + * \date April 2009, updated February 2020 + * \brief Example program to demonstrate the use of the tracking client class + */ + +#ifdef _WIN32 +#include +#define usleep(x) Sleep((x)/1000); +#endif + +#include "trkcli.h" + +using namespace std; + +int main() +{ + // declares an instance of the tracking client + CTrackingClient trk; + + // connects to the server + if (!trk.connect("biorobpc6.epfl.ch", 10502)) + return 1; + } + + // starts a tracking server-side + if (!trk.start_tracking_file("example/test1.csv")) { + return 2; + } + + // displays the first detected point 10 times at 1-sec intervals + for (int i = 0; i<10; i++) { + uint32_t t; + // updates the points from the tracking system + if (!trk.update(t)) { + return 3; + } + // gets the ID of the first point + int id = trk.get_first_id(); + if (id != -1) { + double x, y; + // retrieves the x/y coordinates of that point + if (trk.get_pos(id, x, y)) { + cout << "pt " << id << ": " << x << ", " << y << "." << endl; + } + } + const track_point* pt; + int cnt; + if (trk.get_pos_table(cnt)) { + cout << cnt << " points in table." << endl; + } + usleep(1000000); + } + + // stops the server-side tracking + trk.stop_tracking_file(); +} diff --git a/trkcli.cpp b/trkcli.cpp new file mode 100644 index 0000000..8cdf90c --- /dev/null +++ b/trkcli.cpp @@ -0,0 +1,209 @@ +/** + * \file trkcli.cpp + * \author Alessandro Crespi + * \date 15.4.2009 + * \brief Class for accessing the BIOROB LED tracking system over TCP/IP + */ + +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include "wperror.h" + #define perror wperror +#else + #include + #include + #include + #include + #include + #include + #include + #define closesocket ::close +#endif + +#include "trkcli.h" +#include "netutil.h" + +CTrackingClient::CTrackingClient() +{ +#ifdef _WIN32 + WSADATA ws; + WSAStartup(0x0101, &ws); +#endif + connected = false; + pos_count = 0; + memset(positions, 0, sizeof(positions)); +} + +CTrackingClient::~CTrackingClient() +{ + if (connected) closesocket(sock); +#ifdef _WIN32 + WSACleanup(); +#endif +} + +bool CTrackingClient::connect(const char* hostname, u_short port) +{ + if (connected) { + fprintf(stderr, "Tracking client already connected.\n"); + return false; + } + u_long IP = gethostaddress(hostname); + if (IP==INADDR_NONE) { + fprintf(stderr, "Invalid hostname: %s.\n", hostname); + return false; + } + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock==-1) return false; + + struct sockaddr_in sai; + + sai.sin_family = AF_INET; + sai.sin_port = htons(port); + sai.sin_addr.s_addr = IP; + + if (::connect(sock, (sockaddr*) &sai, sizeof(sai))==-1) { + fprintf(stderr, "Unable to connect to %s:%d.\n", hostname, port); + closesocket(sock); + return false; + } + + int i; + i = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, TCP_NODELAY, (char*) &i, sizeof(i)); +#else + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*) &i, sizeof(i)); +#endif + connected = true; + + return true; +} + +bool CTrackingClient::start_tracking_file(const char* filename) +{ + if (!connected) return false; + + int len = strlen(filename) + 2; + char* buf = new char[len + 1]; + + buf[0] = 'S'; + buf[1] = strlen(filename); + strcpy(&buf[2], filename); + if (send(sock, buf, len, 0)!=len) { + perror("send"); + closesocket(sock); + connected = false; + return false; + } + + if (recv(sock, buf, 1, 0)!=1) { + perror("recv"); + closesocket(sock); + connected = false; + return false; + } + + if (buf[0]!='+') { + fprintf(stderr, "Tracking start failed on the server.\n"); + return false; + } else return true; + +} + +bool CTrackingClient::stop_tracking_file() +{ + if (!connected) return false; + + char c('s'); + if (send(sock, &c, 1, 0)!=1) { + perror("send"); + closesocket(sock); + connected = false; + return false; + } + if (recv(sock, &c, 1, 0)!=1) { + perror("recv"); + closesocket(sock); + connected = false; + return false; + } + if (c!='+') { + fprintf(stderr, "Tracking stop failed on the server.\n"); + return false; + } else return true; + +} + +bool CTrackingClient::update(uint32_t& time) +{ + if (!connected) return false; + + char c('U'); + if (send(sock, &c, 1, 0)!=1) { + perror("send"); + closesocket(sock); + connected = false; + return false; + } + if (recv(sock, (char*) &time, 4, 0)!=4) { + perror("recv"); + closesocket(sock); + connected = false; + return false; + } + if (recv(sock, &c, 1, 0)!=1) { + perror("recv"); + closesocket(sock); + connected = false; + return false; + } + if (c > MAX_POINTS) { + fprintf(stderr, "Error: server wants to return %d spots, only %d allowed.\n", c, MAX_POINTS); + fprintf(stderr, "Connection closed."); + closesocket(sock); + connected = false; + return false; + } + int size = (int) c * sizeof(track_point); + if (block_recv(sock, (uint8_t*) positions, size)!=size) { + fprintf(stderr, "Error while receiving data block from server, closing connection.\n"); + closesocket(sock); + connected = false; + return false; + } else pos_count = c; + + return true; +} + + +bool CTrackingClient::get_pos(const int id, double &x, double &y) +{ + for (int i(0); i0) + return positions[0].id; + else + return -1; +} + +const track_point* CTrackingClient::get_pos_table(int& count) const +{ + count = pos_count; + return positions; +} diff --git a/trkcli.h b/trkcli.h new file mode 100644 index 0000000..6b9e8b2 --- /dev/null +++ b/trkcli.h @@ -0,0 +1,63 @@ +#ifndef __TRKCLI_H +#define __TRKCLI_H + +/** + * \file trkcli.h + * \author Alessandro Crespi + * \date April 2009, updated February 2020 + * \brief Client class to retrieve data from the BIOROB LED tracking system + */ + +#include + +/// Entry containing the position of a detected point +struct track_point { + int id; ///< identifier of the point (not persistent across frames), starts from 100 for 2nd camera + float x; ///< X coordinate (in meters) + float y; ///< Y coordinate (in meters) +} __attribute__((__packed__)); + +const int MAX_POINTS = 40; + +class CTrackingClient { + +public: + + CTrackingClient(); + ~CTrackingClient(); + + /// Connects to the specified tracking server + bool connect(const char* hostname, u_short port = 10502); + + /// Starts a server-side log of all the tracking points + bool start_tracking_file(const char* filename); + /// Stops a previously started tracking log + bool stop_tracking_file(void); + + /// \brief Retrieves the points detected in the current frame + /// \param time The timestamp of the returned frame, in an undefined unit (DO NOT USE FOR TIMING) + /// \return true if the operation succeeded, false in case of an error (e.g. lost connection) + bool update(uint32_t& time); + + /// \brief Gets the identifier of the first detected point (useful when tracking a single point) + /// \return The identifier of the point, or -1 if none are available + int get_first_id(); + + /// Extracts the coordinates of the given point (useful when tracking a single point) + bool get_pos(const int id, double& x, double& y); + + /// \brief Gets a pointer to the table of all detected points in the last retrieved frame + /// \param count Reference to an integer variable where the number of detected points will be written + const track_point* get_pos_table(int& count) const; + +private: + + int sock; + bool connected; + + track_point positions[MAX_POINTS]; + int pos_count; + +}; + +#endif diff --git a/wperror.cpp b/wperror.cpp new file mode 100644 index 0000000..8a53db7 --- /dev/null +++ b/wperror.cpp @@ -0,0 +1,24 @@ +#include +#include + +/** + * \file wperror.cpp + * \author Alessandro Crespi + * \date March 2009 + * \brief perror()-equivalent implementation for Win32 + */ + +void wperror(const char* str) +{ +#ifdef _WIN32 + DWORD err = GetLastError(); + char* msg = NULL; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 60, + NULL, err, LANG_NEUTRAL, (char*) &msg, 2048, NULL); + fprintf(stderr, "%s: %s\n", str, msg); + LocalFree(msg); +#else + perror(str); +#endif +} diff --git a/wperror.h b/wperror.h new file mode 100644 index 0000000..9956c9e --- /dev/null +++ b/wperror.h @@ -0,0 +1,13 @@ +#ifndef __WPERROR_H +#define __WPERROR_H + +/** + * \file wperror.h + * \author Alessandro Crespi + * \date March 2009 + * \brief perror()-equivalent implementation for Win32 + */ + +void wperror(const char* str); + +#endif