#! /usr/bin/env python

import rospy
import time
from threading import Lock
from sensor_msgs.msg import Joy

class Listener:
    def __init__(self, topic_name, topic_type):
        """
        Listener is a wrapper around a subscriber where the callback simply records the latest msg.

        Listener does not consume the message 
            (for consuming behavior, use the standard ros callback pattern)
        Listener does not check timestamps of message headers

        Parameters:
            topic_name (str): name of topic to subscribe to
            topic_type (msg_type): type of message received on topic
        """

        self.data = None
        self.lock = Lock()
            
        self.subscriber = rospy.Subscriber(topic_name, topic_type, self.callback)
        

    def callback(self, msg):
        with self.lock:
            self.data = msg

    def get(self, block_on_none=False):
        """
        Returns the latest msg from the subscribed topic

        Parameters:
        block_on_none (bool): block return until data has been received
        """
        wait_for(lambda: not (block_on_none and self.data is None))
            
        with self.lock:
            return self.data

    
def wait_for(func):
    """
    Waits for function evaluation to be true. Exits cleanly from ros.

    Introduces sleep delay, not recommended for time critical operations
    """
    
    while not func() and not rospy.is_shutdown():
        time.sleep(0.01)

def joy_to_xbox(joy):
    """
    Transforms a joystick sensor_msg to a XBox controller for easier code readability
    
    Parameters:
    joy (sensor_msgs/Joy): xbox msg

    Returns:
    xbox struct where fields are the button names
    """
    class Xbox_msg():
        pass
    x = Xbox_msg()
    x.A, x.B, x.X, x.Y, x.LB, x.RB, \
        x.back, x.start, x.power,\
        x.stick_button_left, x.stick_button_right, \
        x.DL, x.DR, x.DU, x.DD = joy.buttons
    x.LH, x.LV, x.LT, x.RH, x.RV, x.RT, x.DH, x.DV = joy.axes
    return x
        


class Xbox():
    def __init__(self):
        self.xbox_listener = Listener("/joy", Joy)

    def get_buttons_state(self):
        return joy_to_xbox(self.xbox_listener.get(True))

    def wait_for_button(self, button, message=True):
        """
        Waits for button press on xbox.

        Parameters:
        button (str):   Name of xbox button. "A", "B", "X", ...
        message (bool): log a message informing the user?
        """
        if message:
            rospy.loginfo("Waiting for xbox button: " + button)
            
        wait_for(lambda: getattr(self.get_buttons_state(), button))

@@ -163,16 +163,36 @@
 pic_type
+, wait_for_data=True
 ):%0A     
@@ -534,16 +534,21 @@
 e (str):
+     
  name of
@@ -651,37 +651,175 @@
     
-%22%22%22%0A%0A        self.data = None
+    wait_for_data (bool):  'get' method is blocking until valid data has been received%0A        %22%22%22%0A%0A        self.data = None%0A        self.block_on_none = wait_for_data
 %0A   
@@ -1049,29 +1049,8 @@
 self
-, block_on_none=False
 ):%0A 
@@ -1120,101 +1120,8 @@
 opic
-%0A%0A        Parameters:%0A        block_on_none (bool): block return until data has been received
 %0A   
@@ -1159,16 +1159,21 @@
 a: not (
+self.
 block_on
@@ -1535,16 +1535,25 @@
 (0.01)%0A%0A
+        %0A
 def joy_
@@ -2089,25 +2089,16 @@
 eturn x%0A
-        %0A
 %0A%0Aclass 
@@ -2122,24 +2122,46 @@
 _init__(self
+, joystick_topic=%22joy%22
 ):%0A        s
@@ -2193,14 +2193,22 @@
 ner(
-%22/
 joy
-%22
+stick_topic
 , Jo
@@ -2256,55 +2256,383 @@
     
-return joy_to_xbox(self.xbox_listener.get(True)
+%22%22%22%0A        Returns an xbox struct of the last joystick message received%0A        %22%22%22%0A        return joy_to_xbox(self.xbox_listener.get())%0A%0A    def get_button(self, button):%0A        %22%22%22%0A        Return value of button or axis of the controller%0A        0 or 1 for buttons%0A        -1.0 to 1.0 (at most) for axes%0A        %22%22%22%0A        return getattr(self.get_buttons_state(), button
 )%0A%0A 
@@ -3012,24 +3012,20 @@
 lambda: 
-getattr(
+not 
 self.get
@@ -3031,28 +3031,23 @@
 t_button
-s_state(), 
+(
 button)
+ == 0
 )%0A

Change listener to block by default, moved arg to constructor Change Xbox to use relative topic "joy" by default Added get_button function