0

Hi everyone I would like to implement a python ros node that subscribes to a topic (odom in my case) and retransmits part of the message on a socket. I have already implemented such a thing:

#!/usr/bin/env python
import rospy
from std_msgs.msg import String
import socket
import sys
from nav_msgs.msg import Odometry
import struct
from time import sleep
import socket, threading


class ClientThread(threading.Thread):
    def __init__(self,clientAddress,clientsocket):
        threading.Thread.__init__(self)
        self.csocket = clientsocket
        print ("New connection added: ", clientAddress)
        rospy.init_node('serverOdom', anonymous=True)
        rospy.Subscriber('odom',Odometry, callback)
        print ("New subscriber 'severOdom' added: ")

    def run(self):
        rospy.spin()


def callback(msg):
    string=str(msg.pose.pose.position.x)+"\n"+str(msg.pose.pose.position.y)+"\n"
    try:
        ##How i can refeer to the socket??
        self.csocket.send(string.encode())
        print (string)
    except socket.error:
        print ("Error client lost")
        ##How to exit from spin()??


if __name__ == '__main__':
    LOCALHOST = "192.168.2.150"
    PORT = 5005
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((LOCALHOST, PORT))
    print("Server started")
    print("Waiting for client request..")
    while True:
        server.listen(1)
        clientsock, clientAddress = server.accept()
        newthread = ClientThread(clientAddress, clientsock)
        newthread.start()

now I have two problems: The first one that inside the callback function I can't use

self.csocket.send(string.encode())

and i don't know how to send the string to the client.

The second is that if the client disconnects I cannot exit the callback function

some idea? I'm a beginner with python and ros, thanks in advance

EDIT: I founded a solution. maybe it's not elegant but it works.

#!/usr/bin/env python
import rospy
from std_msgs.msg import String
import socket
import sys
from nav_msgs.msg import Odometry
import struct
from time import sleep
import socket, threading


class ClientThread(threading.Thread):
    def __init__(self,clientAddress,clientsocket):
        threading.Thread.__init__(self)
        self.csocket = clientsocket
        print ("New connection added: ", clientAddress)
        rospy.init_node('serverOdom', anonymous=True)
        rospy.Subscriber('odom',Odometry, callback)
        print ("New subscriber 'severOdom' added: ")

    def run(self):
        while True:
            msg = rospy.wait_for_message("odom", Odometry, timeout=None)
            string=str(msg.pose.pose.position.x)+"\n"+str(msg.pose.pose.position.y)+"\n"
            try:
                self.csocket.send(string.encode())
            except socket.error:
                print ("Error client lost", clientAddress)
                return

def callback(msg):
    pass


if __name__ == '__main__':
    LOCALHOST = ""
    PORT = 5005
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((LOCALHOST, PORT))
    print("Server started")
    print("Waiting for client request..")
    while True:
        sleep(2)
        server.listen(5)
        clientsock, clientAddress = server.accept()
        newthread = ClientThread(clientAddress, clientsock)
        newthread.start()

I needed more clients to connect and that when the connection with a client was lost (for example because the client was closed) the thread created by the server ended. with my solution for each thread created by the server a subscriber is also created, probably it would be better to use a single subscriber for all the threads created, but for now I have not been able to do it.

Davide
  • 1
  • 2

1 Answers1

0

If you define the callback as a member of ClientThread, you can access self. Ex

class ClientThread(...):
    def callback(self, msg):
        self.csocket.send(string.encode())

Also, your control flow is all wonky. All the init for the node should happen in main; that's why your confused. Ex of a python node.

Also, your code doesn't quite make sense, as it seems to be both the server and the client. Ex. here is a light rewrite, but it doesn't really make sense & won't behave right. (Except the ros parts should be correct). Like, it seems that you're trying to use threading... to do the job of ros, in which each node is like a thread already.

# server_odom.py

# Ros imports
import rospy
from std_msgs.msg import String
from nav_msgs.msg import Ododmetry

# Sys imports
import sys
import socket
import struct
import threading
from time import sleep

class ClientThread(threading.Thread):
    def __init__(self,clientAddress,clientsocket):
        threading.Thread.__init__(self)
        self.csocket = clientsocket
        print ("New connection added: ", clientAddress)
        rospy.init_node('serverOdom', anonymous=True)
        rospy.Subscriber('odom',Odometry, self.callback)
        print ("New subscriber 'severOdom' added: ")

    def run(self):
        rospy.spin()

    def callback(self, msg):
        string=str(msg.pose.pose.position.x)+"\n"+str(msg.pose.pose.position.y)+"\n"
        try:
            self.csocket.send(string.encode())
            print (string)
        except socket.error:
            print ("Error client lost")
            return

if __name__ == '__main__':
    LOCALHOST = ""
    PORT = 5005
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((LOCALHOST, PORT))
    print("Server started")
    print("Waiting for client request..")
    while True:
        sleep(2)
        server.listen(5)
        clientsock, clientAddress = server.accept()
        newthread = ClientThread(clientAddress, clientsock)
        newthread.start()

If you just mean to format an odometry message to a string or serial buffer, it's significantly less code.

# server_odom_min.py

# Ros imports
import rospy
from std_msgs.msg import String
from nav_msgs.msg import Ododmetry

str_pub = None

def callback(self, msg):
    s = String()
    s.data = str(msg.pose.pose.position.x)+"\n"+str(msg.pose.pose.position.y)+"\n"
    if str_pub:
        str_pub.publish(s)

if __name__ == '__main__':
    rospy.init_node('server_odom_min')
    rospy.Subscriber('odom', Odometry, callback)
    str_pub = rospy.Publisher('odom/string', String, queue_size = 1)
    rospy.spin()

Edit (in response to edit): I see what you're doing now, my bad; the second example doesn't really apply to this question. I edited my first example to reflect what you mean.

JWCS
  • 1,120
  • 1
  • 7
  • 17
  • I edited my question. I will also try to use callback as a member function. thanks for your suggestions – Davide May 30 '20 at 08:01
  • I editted my answer in response: I see what you meant now. But you don't need to make an infinite loop for receiving ros messages, that's what `Subscriber()` and `rospy.spin()` are all about. – JWCS Jun 01 '20 at 01:43