Background TCP/IP Socket Connection and Data Transmission

Hello,
I am currently working with a UR5 CB3 robot. I am wanting to make a .urp file program on the teach pendant that runs two TCP/IP socket connections as a client.

I have my main socket connection running and am able to communicate back and forth with a C++ server program that was written (it handles that data control to allow for the sequential runtime of the UR5 and its motions). I then also want the possibility of having another such program/URScript file, that is capable of sending the values of global variables that are changing in the main socket program (and have it be do so in a background thread process).

All the methods that I have tried to establish such an execution have not been successful.

Is this possible? If so, could I be provided with an example of how I may be able to do so?

The script has been tested with:
Polyscope Version: 5.11 or above
Python 2.7 or above
Reference: The URScript Programming Language
for e-Series 5.12 pages 104 -111
manual.pdf (79.3 KB)

Hi, You may want to recheck the version compatibility but to give you an overall understanding, I will share with you the URScript and the Python file that hopefully does what you want to do.

Otherwise, show us partial source code so that we can visually understand.

The below .urscript sends anything as string/text:

def send_string():
  socket_open("192.168.1.10", 33000, "socket_1")
  socket_send_string("[1,2,3,4]", "socket_1")
  socket_close("socket_1")
end
# Replace with the IP address of your PC and the port you're listening on
send_and_receive_string()
sleep(1)

An the below .py receives what the above from the robot sent to external device.

import socket
def listen_for_string(ip, port):
  # Create a TCP/IP socket
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  # Bind the socket to the port
  server_address = (ip, port)
  sock.bind(server_address)
  # Listen for incoming connections
  sock.listen(1)
  while True:
    # Wait for a connection
    print('Waiting for a connection...')
    connection, client_address = sock.accept()
      try:
      print('Connection from', client_address)
      # Receive the data in small chunks
      data = connection.recv(1024)
      data = data.decode('utf-8')
      array = list(map(int, data.strip('[]').split(',')))
      print('Received "%s"' % data)
      print(array)
      if data:
        print('Sending data back to the client')
        # connection.sendall(response.encode())
        # connection.sendall(json.dumps(response).encode())
  
[manual.pdf|attachment](upload://fT3nbd81mXFNbUB6x99MNQfJKi3.pdf) (79.3 KB)
finally:
  # Clean up the connection
  connection.close()
listen_for_string("192.168.1.164", 33000)
# Replace with the IP address of your PC and the port you're listening on

I shall attach the .script and .urp files for the singular TCP/IP client program running on the teach pendant and have the two C++ server .cpp files as code blocks (one for the main execution, and one for the global variable value receiving).
Client:
ur5_tcp_working_plus_push_parallel.script (6.0 KB)
ur5_tcp_working_plus_push_parallel.urp (3.1 KB)

Main execution TCP/IP socket server:

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int main() {
    int sockfd, newsockfd, portno;
    socklen_t clilen;
    char buffer[1024];
    struct sockaddr_in serv_addr, cli_addr;
    int count = 0;
    
    std::cout << "Starting Program" << std::endl;

    portno = 30000;
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        std::cerr << "Error opening socket" << std::endl;
        return 1;
    }
    
    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);
    
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        std::cerr << "Error binding socket" << std::endl;
        return 1;
    }
    
    std::cout << "Listening" << std::endl;

    listen(sockfd, 5);
    
    clilen = sizeof(cli_addr);
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    if (newsockfd < 0) {
        std::cerr << "Error accepting connection" << std::endl;
        return 1;
    }
    
    std::cout << "Connected to " << inet_ntoa(cli_addr.sin_addr) << std::endl;

    try {
        memset(buffer, 0, sizeof(buffer));
        ssize_t n = read(newsockfd, buffer, sizeof(buffer) - 1);
        if (n < 0) {
            std::cerr << "Error reading from socket" << std::endl;
            return 1;
        }

        std::cout << buffer << std::endl;
        count++;
        std::cout << "The count is: " << count << std::endl;
        usleep(500000);
        std::cout << std::endl;
        usleep(500000);

        std::string firstMessage = "(1.1)";
        ssize_t m1 = write(newsockfd, firstMessage.c_str(), firstMessage.length());
        if (m1 < 0) {
            std::cerr << "Error writing first message to socket" << std::endl;
            return 1;
        }
        std::cout << "Sent 1.1" << std::endl;

        std::string secondMessage = "(100.0,100.0,0.0,0.0,0.0,0.0)";
        ssize_t m2 = write(newsockfd, secondMessage.c_str(), secondMessage.length());
        if (m2 < 0) {
            std::cerr << "Error writing second message to socket" << std::endl;
            return 1;
        }
        std::cout << "Sent (100.0,100.0,0.0,0.0,0.0,0.0)" << std::endl;

        // Wait for client to be ready
        memset(buffer, 0, sizeof(buffer));
        ssize_t n3 = read(newsockfd, buffer, sizeof(buffer) - 1);
        if (n3 < 0) {
            std::cerr << "Error reading from socket" << std::endl;
            return 1;
        }

        std::cout << "Received acknowledgment from the client: " << buffer << std::endl;

        std::string thirdMessage = "(2.2)";
        ssize_t m3 = write(newsockfd, thirdMessage.c_str(), thirdMessage.length());
        if (m3 < 0) {
            std::cerr << "Error writing third message to socket" << std::endl;
            return 1;
        }
        std::cout << "Sent 2.2" << std::endl;


        std::string fourthMessage = "(0.0,0.0,0.0,0.0,0.0,0.0)";
        ssize_t m4 = write(newsockfd, fourthMessage.c_str(), fourthMessage.length());
        if (m4 < 0) {
            std::cerr << "Error writing fourth message to socket" << std::endl;
            return 1;
        }
        std::cout << "Sent (0.0,0.0,0.0,0.0,0.0,0.0)" << std::endl;
        
    } catch (const std::exception &e) {
        std::cerr << e.what() << std::endl;
    }
    
    close(newsockfd);
    close(sockfd);
    
    std::cout << "Program finish" << std::endl;

    return 0;
}

Global variable value receiving TCP/IP socket server:

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <chrono>
#include <ctime>
#include <thread>
#include <curl/curl.h>

// Function to send an SMS using Twilio API
void sendSMS(const std::string& accountSID, const std::string& authToken,
             const std::string& fromNumber, const std::string& toNumber,
             const std::string& message) {
  std::string url = "https://api.twilio.com/2010-04-01/Accounts/" + accountSID +
                    "/Messages.json";

  std::string postFields = "To=" + toNumber + "&From=" + fromNumber +
                           "&Body=" + curl_easy_escape(NULL, message.c_str(), 0);

  CURL* curl = curl_easy_init();
  if (curl) {
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields.c_str());
    curl_easy_setopt(curl, CURLOPT_USERPWD, (accountSID + ":" + authToken).c_str());

    CURLcode res = curl_easy_perform(curl);
    if (res != CURLE_OK)
      std::cerr << "Failed to send SMS: " << curl_easy_strerror(res) << std::endl;

    curl_easy_cleanup(curl);
  }
}

int main() {
  // Twilio account SID and auth token
  std::string accountSID = "{account SID here}";
  std::string authToken = "{account token here}";

  // Phone numbers
  std::string fromNumber = "{from phone number here}";
  std::string toNumber = "{to phone number here}";

  // Message to send
  std::string message = "UR5 STOPPED!";

  int sockfd, newsockfd, portno;
  socklen_t clilen;
  char buffer[1024];
  struct sockaddr_in serv_addr, cli_addr;

  std::cout << "Starting Program" << std::endl;

  portno = 30001;

  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) {
    std::cerr << "Error opening socket" << std::endl;
    return 1;
  }

  int optval = 1;
  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(portno);

  if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
    std::cerr << "Error binding socket" << std::endl;
    return 1;
  }

  std::cout << "Listening" << std::endl;

  listen(sockfd, 5);

  clilen = sizeof(cli_addr);
  newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
  if (newsockfd < 0) {
    std::cerr << "Error accepting connection" << std::endl;
    return 1;
  }

  std::cout << "Connected to " << inet_ntoa(cli_addr.sin_addr) << std::endl;

  try {
    memset(buffer, 0, sizeof(buffer));
    ssize_t n = read(newsockfd, buffer, sizeof(buffer) - 1);
    if (n < 0) {
      std::cerr << "Error reading from socket" << std::endl;
      return 1;
    }

    std::cout << buffer << std::endl;

    // Flag to track acknowledgment status
    bool receivedAcknowledgment = true;

    // Start a thread to monitor acknowledgment
    std::thread acknowledgmentMonitor([&]() {
      std::chrono::seconds timeout(2);
      std::time_t lastAcknowledgmentTime = std::time(nullptr);

      while (true) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        std::time_t currentTime = std::time(nullptr);

        if (currentTime - lastAcknowledgmentTime > timeout.count()) {
          std::cout << "No acknowledgment received for more than 2 seconds. Sending SMS..." << std::endl;
          sendSMS(accountSID, authToken, fromNumber, toNumber, message);
          return 0;
          break;
        }
        else if (receivedAcknowledgment) {
          // Reset the timer if acknowledgment is received
          lastAcknowledgmentTime = std::time(nullptr);
          receivedAcknowledgment = false;
        }
      }
    });

    // Continuously receive acknowledgments from the client
    while (receivedAcknowledgment) {
      memset(buffer, 0, sizeof(buffer));
      ssize_t n = read(newsockfd, buffer, sizeof(buffer) - 1);
      if (n < 0) {
        std::cerr << "Error reading from socket" << std::endl;
        return 1;
      }

      std::string acknowledgment = buffer;
      if (!acknowledgment.empty()) {
        std::cout << "Received acknowledgment from the client: " << acknowledgment << std::endl;
        receivedAcknowledgment = true;
      }
    }

    acknowledgmentMonitor.join();

  } catch (const std::exception &e) {
    std::cerr << e.what() << std::endl;
  }

  close(newsockfd);
  close(sockfd);

  std::cout << "Program finish" << std::endl;

  return 0;
}

I would like for both servers to bind to the different port numbers; that way we have execution of the main program while also being able to transmit any global variables being changed in the main program as a background process (hence why I have it as a thread in the client).

I am not having luck getting both to bind. Executing the processes as separate programs works, but when they are run together, only the main execution socket seems to bind and go through proper execution (while the background socket is waiting to bind).

I’ve observed bizarre behavior in the past when attempting to persist more than a single connection via URScript sockets over threads.

In this example, I ran two Python servers on the same host listening on different ports. When I run the below URScript file which spawns two threads and each opens their own socket, one server drops and the other handles all communication.

thread myHelloThread():
    socket_open("<YOUR_IPv4_ADDRESS>", 8080)
    
    while True:
        sleep(1.5)
        socket_send_string("Hello")
    end
end

thread myWorldThread():
    socket_open("<YOUR_IPv4_ADDRESS>", 8081)
    
    while True:
        sleep(1.5)
        socket_send_string("World")
    end
end

helloThread = run myHelloThread()
worldThread = run myWorldThread()
join helloThread
join worldThread

connection_drop

Side note, I think I see what you’re doing and would suggest looking into UR’s RTDE protocol / server and client library. I forked their repo awhile back and modified it so that when the robot hits a protective stop my Python client sent an email containing the robot state information.