diff --git a/.github/workflows/win-build.yml b/.github/workflows/win-build.yml new file mode 100644 index 000000000..0e88515f8 --- /dev/null +++ b/.github/workflows/win-build.yml @@ -0,0 +1,22 @@ +name: Windows build and tests +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + +jobs: + win-build: + timeout-minutes: 30 + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v4 + - name: configure + run: mkdir build && cd build && cmake .. -DBUILDING_TESTS=1 + #run: mkdir build && cd build && cmake .. -DBUILDING_TESTS=1 -DINTEGRATION_TESTS=1 + - name: build + run: cmake --build build --config Debug + - name: test + run: cd build && ctest --output-on-failure -C Debug diff --git a/3rdparty/endian/endian.h b/3rdparty/endian/endian.h new file mode 100644 index 000000000..3ed95719b --- /dev/null +++ b/3rdparty/endian/endian.h @@ -0,0 +1,103 @@ +// +// endian.h +// +// https://gist.github.com/panzi/6856583 +// +// I, Mathias Panzenböck, place this file hereby into the public domain. Use +// it at your own risk for whatever you like. In case there are +// jurisdictions that don't support putting things in the public domain you +// can also consider it to be "dual licensed" under the BSD, MIT and Apache +// licenses, if you want to. This code is trivial anyway. Consider it an +// example on how to get the endian conversion functions on different +// platforms. + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +// Byte order +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__CYGWIN__) +# include +#elif defined(__APPLE__) +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN +#elif defined(__OpenBSD__) +# include +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) +#elif defined(_WIN32) +# include +# if BYTE_ORDER == LITTLE_ENDIAN +# if defined(_MSC_VER) +# define htobe16(x) _byteswap_ushort(x) +# define htole16(x) (x) +# define be16toh(x) _byteswap_ushort(x) +# define le16toh(x) (x) + +# define htobe32(x) _byteswap_ulong(x) +# define htole32(x) (x) +# define be32toh(x) _byteswap_ulong(x) +# define le32toh(x) (x) + +# define htobe64(x) _byteswap_uint64(x) +# define htole64(x) (x) +# define be64toh(x) _byteswap_uint64(x) +# define le64toh(x) (x) +# elif defined(__GNUC__) || defined(__clang__) +# define htobe16(x) __builtin_bswap16(x) +# define htole16(x) (x) +# define be16toh(x) __builtin_bswap16(x) +# define le16toh(x) (x) + +# define htobe32(x) __builtin_bswap32(x) +# define htole32(x) (x) +# define be32toh(x) __builtin_bswap32(x) +# define le32toh(x) (x) + +# define htobe64(x) __builtin_bswap64(x) +# define htole64(x) (x) +# define be64toh(x) __builtin_bswap64(x) +# define le64toh(x) (x) +# else +# error Compiler is not supported +# endif +# else +# error Byte order is not supported +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN +#else +# error Platform is not supported +#endif + +#endif // PORTABLE_ENDIAN_H__ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e92dc581..3c99f77ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,14 @@ if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) endif() option(WITH_ASAN "Compile with address sanitizer support" OFF) +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) -add_library(urcl SHARED +if(MSVC) +set(BUILD_SHARED_LIBS OFF) +endif() + + +add_library(urcl src/comm/tcp_socket.cpp src/comm/tcp_server.cpp src/control/reverse_interface.cpp @@ -42,12 +48,21 @@ add_library(urcl SHARED src/helpers.cpp ) add_library(ur_client_library::urcl ALIAS urcl) -target_compile_options(urcl PRIVATE -Wall -Wextra -Wno-unused-parameter) target_compile_features(urcl PUBLIC cxx_std_17) -if(WITH_ASAN) - target_compile_options(urcl PUBLIC -fsanitize=address) - target_link_options(urcl PUBLIC -fsanitize=address) + +if(MSVC) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/endian) + target_link_libraries(urcl ws2_32) +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic") + target_compile_options(urcl PRIVATE -Wall -Wextra -Wno-unused-parameter) + + if(WITH_ASAN) + target_compile_options(urcl PUBLIC -fsanitize=address) + target_link_options(urcl PUBLIC -fsanitize=address) + endif() endif() + target_include_directories( urcl PUBLIC $ $ diff --git a/examples/dashboard_example.cpp b/examples/dashboard_example.cpp index 9ce6c3a82..a5a85cf9f 100644 --- a/examples/dashboard_example.cpp +++ b/examples/dashboard_example.cpp @@ -32,11 +32,11 @@ #include #include +#include #include #include #include -#include using namespace urcl; @@ -95,7 +95,11 @@ int main(int argc, char* argv[]) return 1; } +#ifdef _WIN32 + ::Sleep(1000); +#else // _WIN32 sleep(1); +#endif // _WIN32 // Play loaded program if (!my_dashboard->commandPlay()) diff --git a/include/ur_client_library/comm/socket_t.h b/include/ur_client_library/comm/socket_t.h new file mode 100644 index 000000000..c4fd3b56b --- /dev/null +++ b/include/ur_client_library/comm/socket_t.h @@ -0,0 +1,63 @@ +/* + * Copyright 2024, RoboDK Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef _WIN32 + +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#include + +#ifndef TCP_QUICKACK +#define TCP_QUICKACK 12 +#endif + +#ifdef ERROR +#undef ERROR +#endif // ERROR + +typedef SOCKET socket_t; +typedef SSIZE_T ssize_t; + +static inline int ur_setsockopt(socket_t s, int level, int optname, const void* optval, unsigned int optlen) +{ + return ::setsockopt(s, level, optname, reinterpret_cast(optval), static_cast(optlen)); +} + +static inline int ur_close(socket_t s) +{ + return ::closesocket(s); +} + +#else // _WIN32 + +#include +#include +#include +#include + +typedef int socket_t; + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif + +#define ur_setsockopt setsockopt +#define ur_close close + +#endif // _WIN32 diff --git a/include/ur_client_library/comm/stream.h b/include/ur_client_library/comm/stream.h index d10114345..8a573492e 100644 --- a/include/ur_client_library/comm/stream.h +++ b/include/ur_client_library/comm/stream.h @@ -19,9 +19,6 @@ */ #pragma once -#include -#include -#include #include #include #include diff --git a/include/ur_client_library/comm/tcp_server.h b/include/ur_client_library/comm/tcp_server.h index 8cad8ddee..e8f521a27 100644 --- a/include/ur_client_library/comm/tcp_server.h +++ b/include/ur_client_library/comm/tcp_server.h @@ -29,16 +29,15 @@ #ifndef UR_CLIENT_LIBRARY_TCP_SERVER_H_INCLUDED #define UR_CLIENT_LIBRARY_TCP_SERVER_H_INCLUDED -#include -#include -#include -#include #include #include #include #include +#include "ur_client_library/comm/socket_t.h" + + namespace urcl { namespace comm @@ -80,7 +79,7 @@ class TCPServer * \param func Function handling the event information. The file descriptor created by the * connection event will be passed to the function. */ - void setConnectCallback(std::function func) + void setConnectCallback(std::function func) { new_connection_callback_ = func; } @@ -91,7 +90,7 @@ class TCPServer * \param func Function handling the event information. The file descriptor created by the * connection event will be passed to the function. */ - void setDisconnectCallback(std::function func) + void setDisconnectCallback(std::function func) { disconnect_callback_ = func; } @@ -102,7 +101,7 @@ class TCPServer * \param func Function handling the event information. The file client's file_descriptor will be * passed to the function as well as the actual message received from the client. */ - void setMessageCallback(std::function func) + void setMessageCallback(std::function func) { message_callback_ = func; } @@ -133,7 +132,7 @@ class TCPServer * * \returns True on success, false otherwise */ - bool write(const int fd, const uint8_t* buf, const size_t buf_len, size_t& written); + bool write(const socket_t fd, const uint8_t* buf, const size_t buf_len, size_t& written); /*! * \brief Get the maximum number of clients allowed to connect to this server @@ -164,10 +163,10 @@ class TCPServer //! Handles connection events void handleConnect(); - void handleDisconnect(const int fd); + void handleDisconnect(const socket_t fd); //! read data from socket - void readData(const int fd); + void readData(const socket_t fd); //! Event handler. Blocks until activity on any client or connection attempt void spin(); @@ -178,25 +177,22 @@ class TCPServer std::atomic keep_running_; std::thread worker_thread_; - std::atomic listen_fd_; + std::atomic listen_fd_; int port_; - int maxfd_; + socket_t maxfd_; fd_set masterfds_; fd_set tempfds_; uint32_t max_clients_allowed_; - std::vector client_fds_; - - // Pipe for the self-pipe trick (https://cr.yp.to/docs/selfpipe.html) - int self_pipe_[2]; + std::vector client_fds_; static const int INPUT_BUFFER_SIZE = 100; char input_buffer_[INPUT_BUFFER_SIZE]; - std::function new_connection_callback_; - std::function disconnect_callback_; - std::function message_callback_; + std::function new_connection_callback_; + std::function disconnect_callback_; + std::function message_callback_; }; } // namespace comm diff --git a/include/ur_client_library/comm/tcp_socket.h b/include/ur_client_library/comm/tcp_socket.h index 513648106..c166d3791 100644 --- a/include/ur_client_library/comm/tcp_socket.h +++ b/include/ur_client_library/comm/tcp_socket.h @@ -19,15 +19,15 @@ */ #pragma once -#include -#include -#include #include #include #include #include #include +#include "ur_client_library/comm/socket_t.h" + + namespace urcl { namespace comm @@ -49,7 +49,7 @@ enum class SocketState class TCPSocket { private: - std::atomic socket_fd_; + std::atomic socket_fd_; std::atomic state_; std::chrono::milliseconds reconnection_time_; bool reconnection_time_modified_deprecated_ = false; @@ -57,9 +57,9 @@ class TCPSocket void setupOptions(); protected: - static bool open(int socket_fd, struct sockaddr* address, size_t address_len) + static bool open(socket_t socket_fd, struct sockaddr* address, size_t address_len) { - return ::connect(socket_fd, address, address_len) == 0; + return ::connect(socket_fd, address, static_cast(address_len)) == 0; } bool setup(const std::string& host, const int port, const size_t max_num_tries = 0, @@ -90,7 +90,7 @@ class TCPSocket * * \returns The file descriptor of the socket */ - int getSocketFD() + socket_t getSocketFD() { return socket_fd_; } diff --git a/include/ur_client_library/control/reverse_interface.h b/include/ur_client_library/control/reverse_interface.h index 707c4554b..685426c66 100644 --- a/include/ur_client_library/control/reverse_interface.h +++ b/include/ur_client_library/control/reverse_interface.h @@ -142,13 +142,13 @@ class ReverseInterface setKeepaliveCount(const uint32_t count); protected: - virtual void connectionCallback(const int filedescriptor); + virtual void connectionCallback(const socket_t filedescriptor); - virtual void disconnectionCallback(const int filedescriptor); + virtual void disconnectionCallback(const socket_t filedescriptor); - virtual void messageCallback(const int filedescriptor, char* buffer, int nbytesrecv); + virtual void messageCallback(const socket_t filedescriptor, char* buffer, int nbytesrecv); - int client_fd_; + socket_t client_fd_; comm::TCPServer server_; template diff --git a/include/ur_client_library/control/script_command_interface.h b/include/ur_client_library/control/script_command_interface.h index c6cb15f6a..2512c0247 100644 --- a/include/ur_client_library/control/script_command_interface.h +++ b/include/ur_client_library/control/script_command_interface.h @@ -164,11 +164,11 @@ class ScriptCommandInterface : public ReverseInterface } protected: - virtual void connectionCallback(const int filedescriptor) override; + virtual void connectionCallback(const socket_t filedescriptor) override; - virtual void disconnectionCallback(const int filedescriptor) override; + virtual void disconnectionCallback(const socket_t filedescriptor) override; - virtual void messageCallback(const int filedescriptor, char* buffer, int nbytesrecv) override; + virtual void messageCallback(const socket_t filedescriptor, char* buffer, int nbytesrecv) override; private: /*! diff --git a/include/ur_client_library/control/script_sender.h b/include/ur_client_library/control/script_sender.h index 95db332b7..234280394 100644 --- a/include/ur_client_library/control/script_sender.h +++ b/include/ur_client_library/control/script_sender.h @@ -63,13 +63,13 @@ class ScriptSender const std::string PROGRAM_REQUEST_ = std::string("request_program\n"); - void connectionCallback(const int filedescriptor); + void connectionCallback(const socket_t filedescriptor); - void disconnectionCallback(const int filedescriptor); + void disconnectionCallback(const socket_t filedescriptor); - void messageCallback(const int filedescriptor, char* buffer); + void messageCallback(const socket_t filedescriptor, char* buffer); - void sendProgram(const int filedescriptor); + void sendProgram(const socket_t filedescriptor); }; } // namespace control diff --git a/include/ur_client_library/control/trajectory_point_interface.h b/include/ur_client_library/control/trajectory_point_interface.h index a7768a4a0..ddb211a5c 100644 --- a/include/ur_client_library/control/trajectory_point_interface.h +++ b/include/ur_client_library/control/trajectory_point_interface.h @@ -145,11 +145,11 @@ class TrajectoryPointInterface : public ReverseInterface } protected: - virtual void connectionCallback(const int filedescriptor) override; + virtual void connectionCallback(const socket_t filedescriptor) override; - virtual void disconnectionCallback(const int filedescriptor) override; + virtual void disconnectionCallback(const socket_t filedescriptor) override; - virtual void messageCallback(const int filedescriptor, char* buffer, int nbytesrecv) override; + virtual void messageCallback(const socket_t filedescriptor, char* buffer, int nbytesrecv) override; private: std::function handle_trajectory_end_; diff --git a/include/ur_client_library/exceptions.h b/include/ur_client_library/exceptions.h index 5145a75d8..1ccdc4294 100644 --- a/include/ur_client_library/exceptions.h +++ b/include/ur_client_library/exceptions.h @@ -34,6 +34,15 @@ #include #include "ur/version_information.h" +#ifdef _WIN32 +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#ifdef ERROR +#undef ERROR +#endif // ERROR +#endif + namespace urcl { /*! diff --git a/include/ur_client_library/helpers.h b/include/ur_client_library/helpers.h index ad070836e..cf2b133c2 100644 --- a/include/ur_client_library/helpers.h +++ b/include/ur_client_library/helpers.h @@ -29,7 +29,38 @@ #ifndef UR_CLIENT_LIBRARY_HELPERS_H_INCLUDED #define UR_CLIENT_LIBRARY_HELPERS_H_INCLUDED -#include + +#ifdef _WIN32 + +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include + +#ifdef ERROR +#undef ERROR +#endif // ERROR + +#define SCHED_FIFO (1) + +typedef HANDLE pthread_t; + + +static inline pthread_t pthread_self() +{ + return ::GetCurrentThread(); +} + +static inline int sched_get_priority_max(int policy) +{ + (void)policy; + return THREAD_PRIORITY_TIME_CRITICAL; +} + +#else // _WIN32 + +#include + +#endif // _WIN32 namespace urcl { diff --git a/include/ur_client_library/ur/dashboard_client.h b/include/ur_client_library/ur/dashboard_client.h index 579c7bb19..6d4286ed5 100644 --- a/include/ur_client_library/ur/dashboard_client.h +++ b/include/ur_client_library/ur/dashboard_client.h @@ -434,7 +434,6 @@ class DashboardClient : public comm::TCPSocket */ bool commandSaveLog(); -private: /*! * \brief Makes sure that the dashboard_server's version is above the required version * @@ -446,9 +445,6 @@ class DashboardClient : public comm::TCPSocket */ void assertVersion(const std::string& e_series_min_ver, const std::string& cb3_min_ver, const std::string& required_call); - bool send(const std::string& text); - std::string read(); - void rtrim(std::string& str, const std::string& chars = "\t\n\v\f\r "); /*! * \brief Gets the configured receive timeout. If receive timeout is unconfigured "normal" socket timeout of 1 second @@ -458,7 +454,14 @@ class DashboardClient : public comm::TCPSocket */ timeval getConfiguredReceiveTimeout() const; +protected: VersionInformation polyscope_version_; + +private: + bool send(const std::string& text); + std::string read(); + void rtrim(std::string& str, const std::string& chars = "\t\n\v\f\r "); + std::string host_; int port_; std::mutex write_mutex_; diff --git a/include/ur_client_library/ur/ur_driver.h b/include/ur_client_library/ur/ur_driver.h index 45d7dd2fc..e9c6395b5 100644 --- a/include/ur_client_library/ur/ur_driver.h +++ b/include/ur_client_library/ur/ur_driver.h @@ -647,8 +647,13 @@ class UrDriver void resetRTDEClient(const std::string& output_recipe_filename, const std::string& input_recipe_filename, double target_frequency = 0.0, bool ignore_unavailable_outputs = false); -private: static std::string readScriptFile(const std::string& filename); + +protected: + std::unique_ptr> primary_stream_; + std::unique_ptr> secondary_stream_; + +private: /*! * \brief Reconnects the secondary stream used to send program to the robot. * @@ -667,8 +672,6 @@ class UrDriver std::unique_ptr trajectory_interface_; std::unique_ptr script_command_interface_; std::unique_ptr script_sender_; - std::unique_ptr> primary_stream_; - std::unique_ptr> secondary_stream_; double force_mode_gain_scale_factor_ = 0.5; double force_mode_damping_factor_ = 0.025; diff --git a/src/comm/tcp_server.cpp b/src/comm/tcp_server.cpp index ea5122c7e..f143fd280 100644 --- a/src/comm/tcp_server.cpp +++ b/src/comm/tcp_server.cpp @@ -32,12 +32,12 @@ #include #include -#include #include #include #include #include + namespace urcl { namespace comm @@ -45,6 +45,11 @@ namespace comm TCPServer::TCPServer(const int port, const size_t max_num_tries, const std::chrono::milliseconds reconnection_time) : port_(port), maxfd_(0), max_clients_allowed_(0) { +#ifdef _WIN32 + WSAData data; + ::WSAStartup(MAKEWORD(1, 1), &data); +#endif // _WIN32 + init(); bind(max_num_tries, reconnection_time); startListen(); @@ -54,68 +59,56 @@ TCPServer::~TCPServer() { URCL_LOG_DEBUG("Destroying TCPServer object."); shutdown(); - close(listen_fd_); + ur_close(listen_fd_); } void TCPServer::init() { - int err = (listen_fd_ = socket(AF_INET, SOCK_STREAM, 0)); - if (err == -1) + socket_t err = (listen_fd_ = socket(AF_INET, SOCK_STREAM, 0)); + if (err < 0) { throw std::system_error(std::error_code(errno, std::generic_category()), "Failed to create socket endpoint"); } int flag = 1; - setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)); - setsockopt(listen_fd_, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(int)); +#ifndef _WIN32 + ur_setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)); +#endif + ur_setsockopt(listen_fd_, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(int)); URCL_LOG_DEBUG("Created socket with FD %d", (int)listen_fd_); FD_ZERO(&masterfds_); FD_ZERO(&tempfds_); +} - // Create self-pipe for interrupting the worker loop - if (pipe(self_pipe_) == -1) - { - throw std::system_error(std::error_code(errno, std::generic_category()), "Error creating self-pipe"); - } - URCL_LOG_DEBUG("Created read pipe at FD %d", self_pipe_[0]); - FD_SET(self_pipe_[0], &masterfds_); +void TCPServer::shutdown() +{ + keep_running_ = false; - // Make read and write ends of pipe nonblocking - int flags; - flags = fcntl(self_pipe_[0], F_GETFL); - if (flags == -1) - { - throw std::system_error(std::error_code(errno, std::generic_category()), "fcntl-F_GETFL"); - } - flags |= O_NONBLOCK; // Make read end nonblocking - if (fcntl(self_pipe_[0], F_SETFL, flags) == -1) + socket_t shutdown_socket = ::socket(AF_INET, SOCK_STREAM, 0); + if (shutdown_socket == INVALID_SOCKET) { - throw std::system_error(std::error_code(errno, std::generic_category()), "fcntl-F_SETFL"); + throw std::system_error(std::error_code(errno, std::generic_category()), "Unable to create shutdown socket."); } - flags = fcntl(self_pipe_[1], F_GETFL); - if (flags == -1) +#ifdef _WIN32 + unsigned long mode = 1; + ::ioctlsocket(shutdown_socket, FIONBIO, &mode); +#else + int flags = ::fcntl(shutdown_socket, F_GETFL, 0); + if (flags >= 0) { - throw std::system_error(std::error_code(errno, std::generic_category()), "fcntl-F_GETFL"); + ::fcntl(shutdown_socket, F_SETFL, flags | O_NONBLOCK); } - flags |= O_NONBLOCK; // Make write end nonblocking - if (fcntl(self_pipe_[1], F_SETFL, flags) == -1) - { - throw std::system_error(std::error_code(errno, std::generic_category()), "fcntl-F_SETFL"); - } -} +#endif -void TCPServer::shutdown() -{ - keep_running_ = false; + struct sockaddr_in address; + memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + address.sin_port = htons(port_); - // This is basically the self-pipe trick. Writing to the pipe will trigger an event for the event - // handler which will stop the select() call from blocking. - if (::write(self_pipe_[1], "x", 1) == -1 && errno != EAGAIN) - { - throw std::system_error(std::error_code(errno, std::generic_category()), "Writing to self-pipe failed."); - } + ::connect(shutdown_socket, reinterpret_cast(&address), sizeof(address)); // After the event loop has finished the thread will be joinable. if (worker_thread_.joinable()) @@ -160,7 +153,7 @@ void TCPServer::bind(const size_t max_num_tries, const std::chrono::milliseconds URCL_LOG_DEBUG("Bound %d:%d to FD %d", server_addr.sin_addr.s_addr, port_, (int)listen_fd_); FD_SET(listen_fd_, &masterfds_); - maxfd_ = std::max((int)listen_fd_, self_pipe_[0]); + maxfd_ = listen_fd_; } void TCPServer::startListen() @@ -179,8 +172,8 @@ void TCPServer::handleConnect() { struct sockaddr_storage client_addr; socklen_t addrlen = sizeof(client_addr); - int client_fd = accept(listen_fd_, (struct sockaddr*)&client_addr, &addrlen); - if (client_fd < 0) + socket_t client_fd = accept(listen_fd_, (struct sockaddr*)&client_addr, &addrlen); + if (client_fd == INVALID_SOCKET) { std::ostringstream ss; ss << "Failed to accept connection request on port " << port_; @@ -193,7 +186,7 @@ void TCPServer::handleConnect() FD_SET(client_fd, &masterfds_); if (client_fd > maxfd_) { - maxfd_ = std::max(client_fd, self_pipe_[0]); + maxfd_ = client_fd; } if (new_connection_callback_) { @@ -205,7 +198,7 @@ void TCPServer::handleConnect() URCL_LOG_WARN("Connection attempt on port %d while maximum number of clients (%d) is already connected. Closing " "connection.", port_, max_clients_allowed_); - close(client_fd); + ur_close(client_fd); } } @@ -214,7 +207,7 @@ void TCPServer::spin() tempfds_ = masterfds_; // blocks until activity on any socket from tempfds - int sel = select(maxfd_ + 1, &tempfds_, NULL, NULL, NULL); + int sel = select(static_cast(maxfd_ + 1), &tempfds_, NULL, NULL, NULL); if (sel < 0) { URCL_LOG_ERROR("select() failed. Shutting down socket event handler."); @@ -222,30 +215,13 @@ void TCPServer::spin() return; } - // Read part if pipe-trick. This will help interrupting the event handler thread. - if (FD_ISSET(self_pipe_[0], &masterfds_)) + if (!keep_running_) { - URCL_LOG_DEBUG("Activity on self-pipe"); - char buffer; - if (read(self_pipe_[0], &buffer, 1) == -1) - { - while (true) - { - if (errno == EAGAIN) - break; - else - URCL_LOG_ERROR("read failed"); - } - } - else - { - URCL_LOG_DEBUG("Self-pipe triggered"); - return; - } + return; } // Check which fd has an activity - for (int i = 0; i <= maxfd_; i++) + for (socket_t i = 0; i <= maxfd_; i++) { if (FD_ISSET(i, &tempfds_)) { @@ -263,10 +239,10 @@ void TCPServer::spin() } } -void TCPServer::handleDisconnect(const int fd) +void TCPServer::handleDisconnect(const socket_t fd) { URCL_LOG_DEBUG("%d disconnected.", fd); - close(fd); + ur_close(fd); if (disconnect_callback_) { disconnect_callback_(fd); @@ -283,9 +259,9 @@ void TCPServer::handleDisconnect(const int fd) } } -void TCPServer::readData(const int fd) +void TCPServer::readData(const socket_t fd) { - bzero(&input_buffer_, INPUT_BUFFER_SIZE); // clear input buffer + memset(input_buffer_, 0, INPUT_BUFFER_SIZE); // clear input buffer int nbytesrecv = recv(fd, input_buffer_, INPUT_BUFFER_SIZE, 0); if (nbytesrecv > 0) { @@ -331,7 +307,7 @@ void TCPServer::start() worker_thread_ = std::thread(&TCPServer::worker, this); } -bool TCPServer::write(const int fd, const uint8_t* buf, const size_t buf_len, size_t& written) +bool TCPServer::write(const socket_t fd, const uint8_t* buf, const size_t buf_len, size_t& written) { written = 0; @@ -340,7 +316,7 @@ bool TCPServer::write(const int fd, const uint8_t* buf, const size_t buf_len, si // handle partial sends while (written < buf_len) { - ssize_t sent = ::send(fd, buf + written, remaining, 0); + ssize_t sent = ::send(fd, reinterpret_cast(buf + written), static_cast(remaining), 0); if (sent <= 0) { diff --git a/src/comm/tcp_socket.cpp b/src/comm/tcp_socket.cpp index 8803664a8..bce8f9632 100644 --- a/src/comm/tcp_socket.cpp +++ b/src/comm/tcp_socket.cpp @@ -20,15 +20,17 @@ * limitations under the License. */ -#include #include -#include -#include #include #include #include #include +#ifndef _WIN32 +#include +#include +#endif + #include "ur_client_library/log.h" #include "ur_client_library/comm/tcp_socket.h" @@ -36,8 +38,12 @@ namespace urcl { namespace comm { -TCPSocket::TCPSocket() : socket_fd_(-1), state_(SocketState::Invalid), reconnection_time_(std::chrono::seconds(10)) +TCPSocket::TCPSocket() : socket_fd_(INVALID_SOCKET), state_(SocketState::Invalid), reconnection_time_(std::chrono::seconds(10)) { +#ifdef _WIN32 + WSAData data; + ::WSAStartup(MAKEWORD(1, 1), &data); +#endif // _WIN32 } TCPSocket::~TCPSocket() { @@ -47,12 +53,18 @@ TCPSocket::~TCPSocket() void TCPSocket::setupOptions() { int flag = 1; - setsockopt(socket_fd_, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)); - setsockopt(socket_fd_, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(int)); + ur_setsockopt(socket_fd_, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)); + ur_setsockopt(socket_fd_, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(int)); if (recv_timeout_ != nullptr) { - setsockopt(socket_fd_, SOL_SOCKET, SO_RCVTIMEO, recv_timeout_.get(), sizeof(timeval)); +#ifdef _WIN32 + DWORD value = recv_timeout_->tv_sec * 1000; + value += recv_timeout_->tv_usec / 1000; + ur_setsockopt(socket_fd_, SOL_SOCKET, SO_RCVTIMEO, &value, sizeof(value)); +#else + ur_setsockopt(socket_fd_, SOL_SOCKET, SO_RCVTIMEO, recv_timeout_.get(), sizeof(timeval)); +#endif } } @@ -141,8 +153,8 @@ void TCPSocket::close() if (socket_fd_ >= 0) { state_ = SocketState::Closed; - ::close(socket_fd_); - socket_fd_ = -1; + ::ur_close(socket_fd_); + socket_fd_ = INVALID_SOCKET; } } @@ -179,7 +191,7 @@ bool TCPSocket::read(uint8_t* buf, const size_t buf_len, size_t& read) if (state_ != SocketState::Connected) return false; - ssize_t res = ::recv(socket_fd_, buf, buf_len, 0); + ssize_t res = ::recv(socket_fd_, reinterpret_cast(buf), static_cast(buf_len), 0); if (res == 0) { @@ -188,11 +200,20 @@ bool TCPSocket::read(uint8_t* buf, const size_t buf_len, size_t& read) } else if (res < 0) { + res = 0; +#ifdef _WIN32 + int code = ::WSAGetLastError(); + if (code != WSAETIMEDOUT && code != WSAEWOULDBLOCK) + { + state_ = SocketState::Disconnected; + } +#else if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { // any permanent error should be detected early state_ = SocketState::Disconnected; } +#endif return false; } @@ -215,7 +236,7 @@ bool TCPSocket::write(const uint8_t* buf, const size_t buf_len, size_t& written) // handle partial sends while (written < buf_len) { - ssize_t sent = ::send(socket_fd_, buf + written, remaining, 0); + ssize_t sent = ::send(socket_fd_, reinterpret_cast(buf + written), static_cast(remaining), 0); if (sent <= 0) { @@ -223,7 +244,7 @@ bool TCPSocket::write(const uint8_t* buf, const size_t buf_len, size_t& written) return false; } - written += sent; + written += static_cast(sent); remaining -= sent; } diff --git a/src/control/reverse_interface.cpp b/src/control/reverse_interface.cpp index 29c2eb5bf..85c184599 100644 --- a/src/control/reverse_interface.cpp +++ b/src/control/reverse_interface.cpp @@ -35,7 +35,7 @@ namespace control { ReverseInterface::ReverseInterface(uint32_t port, std::function handle_program_state, std::chrono::milliseconds step_time) - : client_fd_(-1) + : client_fd_(INVALID_SOCKET) , server_(port) , handle_program_state_(handle_program_state) , step_time_(step_time) @@ -54,7 +54,7 @@ bool ReverseInterface::write(const vector6d_t* positions, const comm::ControlMod const RobotReceiveTimeout& robot_receive_timeout) { const int message_length = 7; - if (client_fd_ == -1) + if (client_fd_ == INVALID_SOCKET) { return false; } @@ -115,7 +115,7 @@ bool ReverseInterface::writeTrajectoryControlMessage(const TrajectoryControlMess const RobotReceiveTimeout& robot_receive_timeout) { const int message_length = 3; - if (client_fd_ == -1) + if (client_fd_ == INVALID_SOCKET) { return false; } @@ -162,7 +162,7 @@ bool ReverseInterface::writeFreedriveControlMessage(const FreedriveControlMessag const RobotReceiveTimeout& robot_receive_timeout) { const int message_length = 2; - if (client_fd_ == -1) + if (client_fd_ == INVALID_SOCKET) { return false; } @@ -212,9 +212,9 @@ void ReverseInterface::setKeepaliveCount(const uint32_t count) keep_alive_count_modified_deprecated_ = true; } -void ReverseInterface::connectionCallback(const int filedescriptor) +void ReverseInterface::connectionCallback(const socket_t filedescriptor) { - if (client_fd_ < 0) + if (client_fd_ == INVALID_SOCKET) { URCL_LOG_INFO("Robot connected to reverse interface. Ready to receive control commands."); client_fd_ = filedescriptor; @@ -227,14 +227,14 @@ void ReverseInterface::connectionCallback(const int filedescriptor) } } -void ReverseInterface::disconnectionCallback(const int filedescriptor) +void ReverseInterface::disconnectionCallback(const socket_t filedescriptor) { URCL_LOG_INFO("Connection to reverse interface dropped.", filedescriptor); - client_fd_ = -1; + client_fd_ = INVALID_SOCKET; handle_program_state_(false); } -void ReverseInterface::messageCallback(const int filedescriptor, char* buffer, int nbytesrecv) +void ReverseInterface::messageCallback(const socket_t filedescriptor, char* buffer, int nbytesrecv) { URCL_LOG_WARN("Message on ReverseInterface received. The reverse interface currently does not support any message " "handling. This message will be ignored."); diff --git a/src/control/script_command_interface.cpp b/src/control/script_command_interface.cpp index 63c48eabd..ca2fb6d8e 100644 --- a/src/control/script_command_interface.cpp +++ b/src/control/script_command_interface.cpp @@ -227,9 +227,9 @@ bool ScriptCommandInterface::clientConnected() return client_connected_; } -void ScriptCommandInterface::connectionCallback(const int filedescriptor) +void ScriptCommandInterface::connectionCallback(const socket_t filedescriptor) { - if (client_fd_ < 0) + if (client_fd_ == INVALID_SOCKET) { URCL_LOG_DEBUG("Robot connected to ScriptCommandInterface."); client_fd_ = filedescriptor; @@ -242,14 +242,14 @@ void ScriptCommandInterface::connectionCallback(const int filedescriptor) } } -void ScriptCommandInterface::disconnectionCallback(const int filedescriptor) +void ScriptCommandInterface::disconnectionCallback(const socket_t filedescriptor) { URCL_LOG_DEBUG("Connection to ScriptCommandInterface dropped.", filedescriptor); - client_fd_ = -1; + client_fd_ = INVALID_SOCKET; client_connected_ = false; } -void ScriptCommandInterface::messageCallback(const int filedescriptor, char* buffer, int nbytesrecv) +void ScriptCommandInterface::messageCallback(const socket_t filedescriptor, char* buffer, int nbytesrecv) { if (nbytesrecv == 4) { diff --git a/src/control/script_sender.cpp b/src/control/script_sender.cpp index c091ccfc2..92a3023a6 100644 --- a/src/control/script_sender.cpp +++ b/src/control/script_sender.cpp @@ -41,17 +41,17 @@ ScriptSender::ScriptSender(uint32_t port, const std::string& program) server_.start(); } -void ScriptSender::connectionCallback(const int filedescriptor) +void ScriptSender::connectionCallback(const socket_t filedescriptor) { URCL_LOG_DEBUG("New client connected at FD %d.", filedescriptor); } -void ScriptSender::disconnectionCallback(const int filedescriptor) +void ScriptSender::disconnectionCallback(const socket_t filedescriptor) { URCL_LOG_DEBUG("Client at FD %d disconnected.", filedescriptor); } -void ScriptSender::messageCallback(const int filedescriptor, char* buffer) +void ScriptSender::messageCallback(const socket_t filedescriptor, char* buffer) { if (std::string(buffer) == PROGRAM_REQUEST_) { @@ -60,7 +60,7 @@ void ScriptSender::messageCallback(const int filedescriptor, char* buffer) } } -void ScriptSender::sendProgram(const int filedescriptor) +void ScriptSender::sendProgram(const socket_t filedescriptor) { size_t len = program_.size(); const uint8_t* data = reinterpret_cast(program_.c_str()); diff --git a/src/control/trajectory_point_interface.cpp b/src/control/trajectory_point_interface.cpp index 2dcbc013c..ce93c31d6 100644 --- a/src/control/trajectory_point_interface.cpp +++ b/src/control/trajectory_point_interface.cpp @@ -181,9 +181,9 @@ bool TrajectoryPointInterface::writeTrajectorySplinePoint(const vector6d_t* posi return server_.write(client_fd_, buffer, sizeof(buffer), written); } -void TrajectoryPointInterface::connectionCallback(const int filedescriptor) +void TrajectoryPointInterface::connectionCallback(const socket_t filedescriptor) { - if (client_fd_ < 0) + if (client_fd_ == INVALID_SOCKET) { URCL_LOG_DEBUG("Robot connected to trajectory interface."); client_fd_ = filedescriptor; @@ -195,13 +195,13 @@ void TrajectoryPointInterface::connectionCallback(const int filedescriptor) } } -void TrajectoryPointInterface::disconnectionCallback(const int filedescriptor) +void TrajectoryPointInterface::disconnectionCallback(const socket_t filedescriptor) { URCL_LOG_DEBUG("Connection to trajectory interface dropped.", filedescriptor); client_fd_ = -1; } -void TrajectoryPointInterface::messageCallback(const int filedescriptor, char* buffer, int nbytesrecv) +void TrajectoryPointInterface::messageCallback(const socket_t filedescriptor, char* buffer, int nbytesrecv) { if (nbytesrecv == 4) { diff --git a/src/helpers.cpp b/src/helpers.cpp index 2cd1eab10..d08bd9cb0 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -37,6 +37,9 @@ namespace urcl { bool setFiFoScheduling(pthread_t& thread, const int priority) { +#ifdef _WIN32 + return ::SetThreadPriority(thread, priority); +#else // _WIN32 struct sched_param params; params.sched_priority = priority; int ret = pthread_setschedparam(thread, SCHED_FIFO, ¶ms); @@ -88,5 +91,6 @@ bool setFiFoScheduling(pthread_t& thread, const int priority) } } return true; +#endif } } // namespace urcl diff --git a/src/ur/dashboard_client.cpp b/src/ur/dashboard_client.cpp index 8469a766c..75650688f 100644 --- a/src/ur/dashboard_client.cpp +++ b/src/ur/dashboard_client.cpp @@ -29,11 +29,15 @@ #include #include #include -#include #include #include #include +#ifndef _WIN32 +#include +#endif // !_WIN32 + + using namespace std::chrono_literals; namespace urcl @@ -59,7 +63,7 @@ bool DashboardClient::connect(const size_t max_num_tries, const std::chrono::mil timeval configured_tv = getConfiguredReceiveTimeout(); timeval tv; - while (not ret_val) + while (!ret_val) { // The first read after connection can take more time. tv.tv_sec = 10; diff --git a/tests/test_dashboard_client.cpp b/tests/test_dashboard_client.cpp index 1ef235c8c..8b67ff84f 100644 --- a/tests/test_dashboard_client.cpp +++ b/tests/test_dashboard_client.cpp @@ -34,19 +34,34 @@ #include #include "ur_client_library/comm/tcp_socket.h" #include "ur_client_library/ur/version_information.h" -#define private public #include using namespace urcl; std::string g_ROBOT_IP = "192.168.56.101"; +class TestableDashboardClient : public DashboardClient +{ +public: + TestableDashboardClient(const std::string& host) : DashboardClient(host) + { + } + void setPolyscopeVersion(const std::string& version) + { + polyscope_version_ = VersionInformation::fromString(version); + } + VersionInformation getPolyscopeVersion() + { + return polyscope_version_; + } +}; + class DashboardClientTest : public ::testing::Test { protected: void SetUp() { - dashboard_client_.reset(new DashboardClient(g_ROBOT_IP)); + dashboard_client_.reset(new TestableDashboardClient(g_ROBOT_IP)); } void TearDown() @@ -54,7 +69,7 @@ class DashboardClientTest : public ::testing::Test dashboard_client_.reset(); } - std::unique_ptr dashboard_client_; + std::unique_ptr dashboard_client_; }; TEST_F(DashboardClientTest, connect) @@ -143,11 +158,11 @@ TEST_F(DashboardClientTest, e_series_version) { std::string msg; EXPECT_TRUE(dashboard_client_->connect()); - if (!dashboard_client_->polyscope_version_.isESeries()) + if (!dashboard_client_->getPolyscopeVersion().isESeries()) GTEST_SKIP(); - dashboard_client_->polyscope_version_ = VersionInformation::fromString("5.0.0"); + dashboard_client_->setPolyscopeVersion("5.0.0"); EXPECT_THROW(dashboard_client_->commandSafetyStatus(msg), UrException); - dashboard_client_->polyscope_version_ = VersionInformation::fromString("5.5.0"); + dashboard_client_->setPolyscopeVersion("5.5.0"); EXPECT_TRUE(dashboard_client_->commandSafetyStatus(msg)); EXPECT_THROW(dashboard_client_->commandSetUserRole("none"), UrException); } @@ -157,11 +172,12 @@ TEST_F(DashboardClientTest, cb3_version) { std::string msg; EXPECT_TRUE(dashboard_client_->connect()); - if (dashboard_client_->polyscope_version_.isESeries()) + if (dashboard_client_->getPolyscopeVersion().isESeries()) GTEST_SKIP(); - dashboard_client_->polyscope_version_ = VersionInformation::fromString("1.6.0"); + + dashboard_client_->setPolyscopeVersion("1.6.0"); EXPECT_THROW(dashboard_client_->commandIsProgramSaved(), UrException); - dashboard_client_->polyscope_version_ = VersionInformation::fromString("1.8.0"); + dashboard_client_->setPolyscopeVersion("1.8.0"); EXPECT_TRUE(dashboard_client_->commandIsProgramSaved()); EXPECT_THROW(dashboard_client_->commandIsInRemoteControl(), UrException); } diff --git a/tests/test_pipeline.cpp b/tests/test_pipeline.cpp index 327b3b554..4f597fa77 100644 --- a/tests/test_pipeline.cpp +++ b/tests/test_pipeline.cpp @@ -68,7 +68,7 @@ class PipelineTest : public ::testing::Test server_.reset(); } - void connectionCallback(const int filedescriptor) + void connectionCallback(const socket_t filedescriptor) { std::lock_guard lk(connect_mutex_); client_fd_ = filedescriptor; @@ -89,7 +89,7 @@ class PipelineTest : public ::testing::Test } std::unique_ptr server_; - int client_fd_; + socket_t client_fd_; std::unique_ptr> stream_; std::unique_ptr parser_; @@ -247,7 +247,7 @@ TEST_F(PipelineTest, connect_non_connected_robot) auto end = std::chrono::system_clock::now(); auto elapsed = end - start; // This is only a rough estimate, obviously - EXPECT_LT(elapsed, std::chrono::milliseconds(1500)); + EXPECT_LT(elapsed, std::chrono::milliseconds(7500)); } int main(int argc, char* argv[]) diff --git a/tests/test_producer.cpp b/tests/test_producer.cpp index 5ceaec4e0..9d9a398f4 100644 --- a/tests/test_producer.cpp +++ b/tests/test_producer.cpp @@ -55,7 +55,7 @@ class ProducerTest : public ::testing::Test server_.reset(); } - void connectionCallback(const int filedescriptor) + void connectionCallback(const socket_t filedescriptor) { std::lock_guard lk(connect_mutex_); client_fd_ = filedescriptor; @@ -76,7 +76,7 @@ class ProducerTest : public ::testing::Test } std::unique_ptr server_; - int client_fd_; + socket_t client_fd_; private: std::condition_variable connect_cv_; @@ -133,7 +133,7 @@ TEST_F(ProducerTest, connect_non_connected_robot) auto end = std::chrono::system_clock::now(); auto elapsed = end - start; // This is only a rough estimate, obviously - EXPECT_LT(elapsed, std::chrono::milliseconds(1500)); + EXPECT_LT(elapsed, std::chrono::milliseconds(7500)); } int main(int argc, char* argv[]) diff --git a/tests/test_rtde_writer.cpp b/tests/test_rtde_writer.cpp index 070e99b1f..bd289508c 100644 --- a/tests/test_rtde_writer.cpp +++ b/tests/test_rtde_writer.cpp @@ -65,7 +65,7 @@ class RTDEWriterTest : public ::testing::Test server_.reset(); } - void messageCallback(const int filedescriptor, char* buffer, int nbytesrecv) + void messageCallback(const socket_t filedescriptor, char* buffer, int nbytesrecv) { std::lock_guard lk(message_mutex_); uint8_t* buf = reinterpret_cast(buffer); diff --git a/tests/test_stream.cpp b/tests/test_stream.cpp index 6368d0d80..853228bb6 100644 --- a/tests/test_stream.cpp +++ b/tests/test_stream.cpp @@ -58,7 +58,7 @@ class StreamTest : public ::testing::Test } // callback functions for the tcp server - void messageCallback(const int filedescriptor, char* buffer, size_t nbytesrecv) + void messageCallback(const socket_t filedescriptor, char* buffer, size_t nbytesrecv) { std::lock_guard lk(message_mutex_); read_ = nbytesrecv; @@ -67,7 +67,7 @@ class StreamTest : public ::testing::Test message_callback_ = true; } - void connectionCallback(const int filedescriptor) + void connectionCallback(const socket_t filedescriptor) { std::lock_guard lk(connect_mutex_); client_fd_ = filedescriptor; @@ -100,7 +100,7 @@ class StreamTest : public ::testing::Test } std::unique_ptr server_; - int client_fd_; + socket_t client_fd_; std::string received_message_; size_t read_; @@ -329,7 +329,7 @@ TEST_F(StreamTest, connect_non_connected_robot) auto end = std::chrono::system_clock::now(); auto elapsed = end - start; // This is only a rough estimate, obviously - EXPECT_LT(elapsed, std::chrono::milliseconds(1500)); + EXPECT_LT(elapsed, std::chrono::milliseconds(7500)); } int main(int argc, char* argv[]) diff --git a/tests/test_tcp_server.cpp b/tests/test_tcp_server.cpp index 0cec4b211..77fb916a9 100644 --- a/tests/test_tcp_server.cpp +++ b/tests/test_tcp_server.cpp @@ -77,7 +77,7 @@ class TCPServerTest : public ::testing::Test }; // callback functions - void connectionCallback(const int filedescriptor) + void connectionCallback(const socket_t filedescriptor) { std::lock_guard lk(connect_mutex_); client_fd_ = filedescriptor; @@ -85,15 +85,15 @@ class TCPServerTest : public ::testing::Test connection_callback_ = true; } - void disconnectionCallback(const int filedescriptor) + void disconnectionCallback(const socket_t filedescriptor) { std::lock_guard lk(disconnect_mutex_); - client_fd_ = -1; + client_fd_ = INVALID_SOCKET; disconnect_cv_.notify_one(); disconnection_callback_ = true; } - void messageCallback(const int filedescriptor, char* buffer) + void messageCallback(const socket_t filedescriptor, char* buffer) { std::lock_guard lk(message_mutex_); message_ = std::string(buffer); @@ -148,7 +148,7 @@ class TCPServerTest : public ::testing::Test int port_ = 50001; std::string message_ = ""; - int client_fd_ = -1; + socket_t client_fd_ = INVALID_SOCKET; private: std::condition_variable connect_cv_; diff --git a/tests/test_tcp_socket.cpp b/tests/test_tcp_socket.cpp index a96b6595b..5e71b885c 100644 --- a/tests/test_tcp_socket.cpp +++ b/tests/test_tcp_socket.cpp @@ -63,7 +63,7 @@ class TCPSocketTest : public ::testing::Test } // callback functions for the tcp server - void messageCallback(const int filedescriptor, char* buffer) + void messageCallback(const socket_t filedescriptor, char* buffer) { std::lock_guard lk(message_mutex_); received_message_ = std::string(buffer); @@ -71,7 +71,7 @@ class TCPSocketTest : public ::testing::Test message_callback_ = true; } - void connectionCallback(const int filedescriptor) + void connectionCallback(const socket_t filedescriptor) { std::lock_guard lk(connect_mutex_); client_fd_ = filedescriptor; @@ -159,7 +159,7 @@ class TCPSocketTest : public ::testing::Test }; std::string received_message_; - int client_fd_; + socket_t client_fd_; std::unique_ptr server_; std::unique_ptr client_; @@ -351,7 +351,7 @@ TEST_F(TCPSocketTest, connect_non_running_robot) auto end = std::chrono::system_clock::now(); auto elapsed = end - start; // This is only a rough estimate, obviously - EXPECT_LT(elapsed, std::chrono::milliseconds(1500)); + EXPECT_LT(elapsed, std::chrono::milliseconds(7500)); } TEST_F(TCPSocketTest, test_deprecated_reconnection_time_interface) @@ -374,7 +374,7 @@ TEST_F(TCPSocketTest, test_read_on_socket_abruptly_closed) server_->write(client_fd_, data, len, written); // Simulate socket failure - close(client_->getSocketFD()); + ur_close(client_->getSocketFD()); char characters; size_t read_chars = 0; diff --git a/tests/test_ur_driver.cpp b/tests/test_ur_driver.cpp index 8ec2b989d..1c236c3f5 100644 --- a/tests/test_ur_driver.cpp +++ b/tests/test_ur_driver.cpp @@ -31,7 +31,6 @@ #include #include -#define private public #include using namespace urcl; @@ -42,7 +41,24 @@ const std::string INPUT_RECIPE = "resources/rtde_input_recipe.txt"; const std::string CALIBRATION_CHECKSUM = "calib_12788084448423163542"; std::string g_ROBOT_IP = "192.168.56.101"; -std::unique_ptr g_ur_driver; +class TestableUrDriver : public UrDriver +{ +public: + TestableUrDriver(const std::string& robot_ip, const std::string& script_file, const std::string& output_recipe_file, + const std::string& input_recipe_file, std::function handle_program_state, + bool headless_mode, std::unique_ptr tool_comm_setup, + const std::string& calibration_checksum) + : UrDriver(robot_ip, script_file, output_recipe_file, input_recipe_file, handle_program_state, headless_mode, + std::move(tool_comm_setup), calibration_checksum) + { + } + void closeSecondaryStream() + { + secondary_stream_->close(); + } +}; + +std::unique_ptr g_ur_driver; std::unique_ptr g_dashboard_client; bool g_program_running; @@ -118,16 +134,18 @@ class UrDriverTest : public ::testing::Test const bool headless = true; try { - g_ur_driver.reset(new UrDriver(g_ROBOT_IP, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState, - headless, std::move(tool_comm_setup), CALIBRATION_CHECKSUM)); + g_ur_driver.reset(new TestableUrDriver(g_ROBOT_IP, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, + &handleRobotProgramState, headless, std::move(tool_comm_setup), + CALIBRATION_CHECKSUM)); } catch (UrException& exp) { std::cout << "caught exception " << exp.what() << " while launch driver, retrying once in 10 seconds" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(10)); - g_ur_driver.reset(new UrDriver(g_ROBOT_IP, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState, - headless, std::move(tool_comm_setup), CALIBRATION_CHECKSUM)); + g_ur_driver.reset(new TestableUrDriver(g_ROBOT_IP, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, + &handleRobotProgramState, headless, std::move(tool_comm_setup), + CALIBRATION_CHECKSUM)); } g_ur_driver->startRTDECommunication(); // Setup rtde read thread @@ -210,6 +228,9 @@ TEST_F(UrDriverTest, read_non_existing_script_file) TEST_F(UrDriverTest, read_existing_script_file) { +#ifdef _WIN32 +#define mkstemp _mktemp_s +#endif g_consume_rtde_packages = true; char existing_script_file[] = "urscript.XXXXXX"; int fd = mkstemp(existing_script_file); @@ -367,7 +388,7 @@ TEST_F(UrDriverTest, send_robot_program_retry_on_failure) // Check that sendRobotProgram is robust to the secondary stream being disconnected. This is what happens when // switching from Remote to Local and back to Remote mode for example. - g_ur_driver->secondary_stream_->close(); + g_ur_driver->closeSecondaryStream(); EXPECT_TRUE(g_ur_driver->sendRobotProgram()); }