From c675113891bbb506f2d5249c78b7a3b650c5bb24 Mon Sep 17 00:00:00 2001
From: Stuart Glaser <sglaser@willowgarage.com>
Date: Tue, 3 Apr 2012 18:54:12 -0700
Subject: [PATCH] Driver no longer fights with initialization.  Listens to the
 binary data stream from the robot.

---
 ur5_driver/driver.py | 135 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 115 insertions(+), 20 deletions(-)

diff --git a/ur5_driver/driver.py b/ur5_driver/driver.py
index 7c0484d..8a29422 100755
--- a/ur5_driver/driver.py
+++ b/ur5_driver/driver.py
@@ -15,7 +15,10 @@ from sensor_msgs.msg import JointState
 from control_msgs.msg import FollowJointTrajectoryAction
 from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
 
-PORT=30001
+from deserialize import RobotState, RobotMode
+
+PORT=30002
+REVERSE_PORT = 50001
 
 MSG_OUT = 1
 MSG_QUIT = 2
@@ -40,7 +43,7 @@ connected_robot = None
 connected_robot_lock = threading.Lock()
 connected_robot_cond = threading.Condition(connected_robot_lock)
 pub_joint_states = rospy.Publisher('joint_states', JointState)
-dump_state = open('dump_state', 'wb')
+#dump_state = open('dump_state', 'wb')
 
 class EOF(Exception): pass
 
@@ -58,6 +61,106 @@ def dumpstacks():
 def log(s):
     print "[%s] %s" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'), s)
 
+
+class UR5Connection(object):
+    TIMEOUT = 1.0
+    
+    DISCONNECTED = 0
+    CONNECTED = 1
+    READY_TO_PROGRAM = 2
+    EXECUTING = 3
+    
+    def __init__(self, hostname, port, program):
+        self.__thread = None
+        self.__sock = None
+        self.robot_state = self.DISCONNECTED
+        self.hostname = hostname
+        self.port = port
+        self.program = program
+        self.last_state = None
+
+    def connect(self):
+        if self.__sock:
+            self.disconnect()
+        self.__buf = ""
+        self.robot_state = self.CONNECTED
+        self.__sock = socket.create_connection((self.hostname, self.port))
+        self.__keep_running = True
+        self.__thread = threading.Thread(name="UR5Connection", target=self.__run)
+        self.__thread.daemon = True
+        self.__thread.start()
+
+    def send_program(self):
+        assert self.robot_state == self.READY_TO_PROGRAM
+        rospy.loginfo("Programming the robot at %s" % self.hostname)
+        self.__sock.sendall(self.program)
+        self.robot_state = self.EXECUTING
+        
+    def disconnect(self):
+        if self.__thread:
+            self.__keep_running = False
+            self.__thread.join()
+            self.__thread = None
+        if self.__sock:
+            self.__sock.close()
+            self.__sock = None
+        self.last_state = None
+        self.robot_state = self.DISCONNECTED
+
+    def ready_to_program(self):
+        return (self.robot_state == self.READY_TO_PROGRAM)
+
+    def __trigger_disconnected(self):
+        log("Robot disconnected")
+        self.robot_state = self.DISCONNECTED
+    def __trigger_ready_to_program(self):
+        rospy.loginfo("Robot ready to program")
+    def __trigger_halted(self):
+        log("Halted")
+
+    def __on_packet(self, buf):
+        state = RobotState.unpack(buf)
+        self.last_state = state
+        #import deserialize; deserialize.pstate(self.last_state)
+
+        #log("Packet.  Mode=%s" % state.robot_mode_data.robot_mode)
+
+        can_execute = (state.robot_mode_data.robot_mode in [RobotMode.READY, RobotMode.RUNNING])
+        #print "can execute:", can_execute, "  state:", self.robot_state
+        if self.robot_state == self.CONNECTED:
+            if can_execute:
+                self.__trigger_ready_to_program()
+                self.robot_state = self.READY_TO_PROGRAM
+        elif self.robot_state == self.READY_TO_PROGRAM:
+            if not can_execute:
+                self.robot_state = self.CONNECTED
+        elif self.robot_state == self.EXECUTING:
+            if not can_execute:
+                self.__trigger_halted()
+                self.robot_state = self.CONNECTED
+
+    def __run(self):
+        while self.__keep_running:
+            r, _, _ = select.select([self.__sock], [], [], self.TIMEOUT)
+            if r:
+                more = self.__sock.recv(4096)
+                if more:
+                    self.__buf = self.__buf + more
+
+                    # Attempts to extract a packet
+                    packet_length, ptype = struct.unpack_from("!IB", self.__buf)
+                    if packet_length >= len(self.__buf):
+                        packet, self.__buf = self.__buf[:packet_length], self.__buf[packet_length:]
+                        self.__on_packet(packet)
+                else:
+                    self.__trigger_disconnected()
+                    self.__keep_running = False
+                    
+            else:
+                self.__trigger_disconnected()
+                self.__keep_running = False
+
+
 def setConnectedRobot(r):
     global connected_robot, connected_robot_lock
     with connected_robot_lock:
@@ -379,9 +482,7 @@ class UR5TrajectoryFollower(object):
                     self.goal_handle.set_succeeded()
                     self.goal_handle = None
 
-sock = None
 def main():
-    global sock
     rospy.init_node('ur5_driver', disable_signals=True)
     if rospy.get_param("use_sim_time", False):
         rospy.logfatal("use_sim_time is set!!!")
@@ -399,6 +500,11 @@ def main():
     thread_commander = threading.Thread(name="CommanderHandler", target=server.serve_forever)
     thread_commander.daemon = True
     thread_commander.start()
+
+    with open('prog') as fin:
+        program = fin.read() % {"driver_hostname": socket.getfqdn()}
+    connection = UR5Connection(robot_hostname, PORT, program)
+    connection.connect()
     
     action_server = None
     try:
@@ -413,22 +519,11 @@ def main():
 
                 rospy.loginfo("Programming the robot")
                 while True:
-                # Sends the program to the robot
-                    sock = socket.create_connection((robot_hostname, PORT))
-                    with open('prog') as fin:
-                        program = fin.read()
-                        sock.sendall(program % {"driver_hostname": socket.getfqdn()})
-
-                    # Useful for determining if the robot failed to compile the program
-                    if False:
-                        print "Dump"
-                        print "="*70
-                        o = ""
-                        while len(o) < 4096:
-                            r = sock.recv(1024)
-                            if not r: break
-                            o = o + r
-                        print o
+                    # Sends the program to the robot
+                    while not connection.ready_to_program():
+                        print "Waiting to program"
+                        time.sleep(1.0)
+                    connection.send_program()
 
                     r = getConnectedRobot(wait=True, timeout=1.0)
                     if r:
-- 
GitLab