Diff of /URBasic/realTimeClient.py [000000] .. [c1b1c5]

Switch to side-by-side view

--- a
+++ b/URBasic/realTimeClient.py
@@ -0,0 +1,310 @@
+'''
+Python 3.x library to control an UR robot through its TCP/IP interfaces
+Copyright (C) 2017  Martin Huus Bjerge, Rope Robotics ApS, Denmark
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
+and associated documentation files (the "Software"), to deal in the Software without restriction, 
+including without limitation the rights to use, copy, modify, merge, publish, distribute, 
+sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 
+is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies 
+or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL "Rope Robotics ApS" BE LIABLE FOR ANY CLAIM, 
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of "Rope Robotics ApS" shall not be used 
+in advertising or otherwise to promote the sale, use or other dealings in this Software 
+without prior written authorization from "Rope Robotics ApS".
+'''
+__author__ = "Martin Huus Bjerge"
+__copyright__ = "Copyright 2017, Rope Robotics ApS, Denmark"
+__license__ = "MIT License"
+
+import URBasic
+import socket
+import threading
+import select
+import re
+import numpy as np
+import time
+
+DEFAULT_TIMEOUT = 1.0
+
+class ConnectionState:
+    ERROR = 0
+    DISCONNECTED = 1
+    CONNECTED = 2
+    PAUSED = 3
+    STARTED = 4
+
+class RealTimeClient(object):
+    '''
+    Interface to UR robot Real Time Client interface.
+    For more detailes see this site:
+    http://www.universal-robots.com/how-tos-and-faqs/how-to/ur-how-tos/remote-control-via-tcpip-16496/
+    
+    The Real Time Client in this version is only used to send program and script commands 
+    to the robot, not to read data from the robot, all data reading is done via the RTDE interface.
+    
+    The constructor takes a UR robot hostname as input, and a RTDE configuration file.
+
+    Input parameters:
+    host (string):  hostname or IP of UR Robot (RT CLient server)
+    conf_filename (string):  Path to xml file describing what channels to activate
+    logger (URBasis_DataLogging obj): A instance if a logger object if common logging is needed.
+
+    
+    Example:
+    rob = URBasic.realTimeClient.RT_CLient('192.168.56.101')
+    self.close_rtc()
+    '''
+
+
+    def __init__(self, robotModel):
+        '''
+        Constructor see class description for more info.
+        '''
+        if(False):
+            assert isinstance(robotModel, URBasic.robotModel.RobotModel)  ### This line is to get code completion for RobotModel
+        self.__robotModel = robotModel
+
+        logger = URBasic.dataLogging.DataLogging()
+        name = logger.AddEventLogging(__name__, log2Consol=False,level = URBasic.logging.WARNING)
+        self.__logger = logger.__dict__[name]
+        self.__robotModel.rtcConnectionState = ConnectionState.DISCONNECTED
+        self.__reconnectTimeout = 60
+        self.__sock = None
+        self.__thread = None
+        if self.__connect():
+            self.__logger.info('RT_CLient constructor done')
+        else:
+            self.__logger.info('RT_CLient constructor done but not connected')
+        
+    def __connect(self):
+        '''
+        Initialize RT Client connection to host .
+        
+        Return value:
+        success (boolean)
+        
+        Example:
+        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101')
+        rob.connect()
+        '''       
+        if self.__sock:
+            return True
+
+        t0 = time.time()
+        while (time.time()-t0<self.__reconnectTimeout) and self.__robotModel.rtcConnectionState < ConnectionState.CONNECTED:
+            try:
+                self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)            
+                self.__sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)         
+                self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+                self.__sock.settimeout(DEFAULT_TIMEOUT)
+                self.__sock.connect((self.__robotModel.ipAddress, 30003))
+                self.__robotModel.rtcConnectionState = ConnectionState.CONNECTED
+                time.sleep(0.5)
+                self.__logger.info('Connected')
+                return True
+            except (socket.timeout, socket.error):
+                self.__sock = None
+                self.__logger.error('RTC connecting')
+
+        return False
+                
+
+    def Disconnect(self):
+        '''
+        Disconnect the RT Client connection.
+        '''        
+        if self.__sock:
+            self.__sock.close()
+            self.__sock = None
+            self.__logger.info('Disconnected')
+        self.__robotModel.rtcConnectionState = ConnectionState.DISCONNECTED
+        return True
+
+
+    def IsRtcConnected(self):
+        '''
+        Returns True if the connection is open.
+
+        Return value:
+        status (boolean): True if connected and False of not connected.
+
+        Example:
+        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101')
+        rob.connect()
+        print(rob.is_connected())
+        rob.disconnect()
+        '''
+        return self.__robotModel.rtcConnectionState > ConnectionState.DISCONNECTED
+        
+    def SendProgram(self,prg=''):
+        '''
+        Send a new command or program (string) to the UR controller. 
+        The command or program will be executed as soon as it's received by the UR controller. 
+        Sending a new command or program while stop and existing running command or program and start the new one.
+        The program or command will also bee modified to include some control signals to be used
+        for monitoring if a program execution is successful and finished.  
+
+        Input parameters:
+        prg (string): A string containing a single command or a whole program.
+
+        Example:
+        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101',logger=logger)
+        rob.connect()
+        rob.send_srt('set_digital_out(0, True)')
+        rob.disconnect()        
+        '''
+        if not self.IsRtcConnected():
+            if not self.__connect():
+                self.__logger.error('SendProgram: Not connected to robot')
+ 
+        if self.__robotModel.stopRunningFlag:
+            self.__logger.info('SendProgram: Send program aborted due to stopRunningFlag')
+            return
+ 
+        #Close down previous thread 
+        if self.__thread is not None:
+            if self.__robotModel.rtcProgramRunning:
+                self.__robotModel.stopRunningFlag = True
+                while self.__robotModel.rtcProgramRunning: time.sleep(0.1)
+                self.__robotModel.stopRunningFlag = False
+            self.__thread.join()
+            
+        
+        #Rest status bits
+        self.__robotModel.rtcProgramRunning = True
+        self.__robotModel.rtcProgramExecutionError = False
+        
+        #Send and wait from program
+        self.__sendPrg(self.__AddStatusBit2Prog(prg))        
+        self.__thread = threading.Thread(target=self.__waitForProgram2Finish, kwargs={'prg': prg})
+        self.__thread.start()
+        #self.__waitForProgram2Finish(prg)
+            
+    def Send(self,prg=''):
+        '''
+        Send a new command (string) to the UR controller. 
+        The command or program will be executed as soon as it's received by the UR controller. 
+        Sending a new command or program while stop and existing running command or program and start the new one.
+        The program or command will also bee modified to include some control signals to be used
+        for monitoring if a program execution is successful and finished.  
+
+        Input parameters:
+        prg (string): A string containing a single command or a whole program.
+
+
+        Example:
+        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101',logger=logger)
+        rob.connect()
+        rob.send_srt('set_digital_out(0, True)')
+        rob.disconnect()        
+        '''
+        if not self.IsRtcConnected():
+            if not self.__connect():
+                self.__logger.error('SendProgram: Not connected to robot')
+        if self.__robotModel.stopRunningFlag:
+            self.__logger.info('SendProgram: Send command aborted due to stopRunningFlag')
+            return
+    
+        #Rest status bits
+        self.__robotModel.rtcProgramRunning = True
+        self.__robotModel.rtcProgramExecutionError = False
+        
+        #Send
+        self.__sendPrg(prg)      
+        self.__robotModel.rtcProgramRunning = False
+
+    def __AddStatusBit2Prog(self,prg):
+        '''
+        Modifying program to include status bit's in beginning and end of program
+        '''
+        def1 = prg.find('def ')
+        if def1>=0:
+            prglen = len(prg)
+            prg = prg.replace('):\n', '):\n  write_output_boolean_register(0, True)\n',1)
+            if len(prg) == prglen:
+                self.__logger.warning('Send_program: Syntax error in program')
+                return False
+                
+            if (len(re.findall('def ', prg)))>1:
+                mainprg = prg[0:prg[def1+4:].find('def ')+def1+4]
+                mainPrgEnd = (np.max([mainprg.rfind('end '), mainprg.rfind('end\n')]))
+                prg = prg.replace(prg[0:mainPrgEnd], prg[0:mainPrgEnd] + '\n  write_output_boolean_register(1, True)\n',1)
+            else:
+                mainPrgEnd = prg.rfind('end')
+                prg = prg.replace(prg[0:mainPrgEnd], prg[0:mainPrgEnd] + '\n  write_output_boolean_register(1, True)\n',1)
+                
+        else:
+            prg = 'def script():\n  write_output_boolean_register(0, True)\n  ' + prg + '\n  write_output_boolean_register(1, True)\nend\n'
+        return prg
+        
+    def __sendPrg(self,prg):
+        '''
+        Sending program str via socket
+        '''
+        programSend = False      
+        self.__robotModel.forceRemoteActiveFlag = False
+        while not self.__robotModel.stopRunningFlag and not programSend:
+            try:
+                (_, writable, _) = select.select([], [self.__sock], [], DEFAULT_TIMEOUT)
+                if len(writable):
+                    self.__sock.send(prg.encode())
+                    self.__logger.info('Program send to Robot:\n' + prg)
+                    programSend = True
+            except:
+                self.__sock = None
+                self.__robotModel.rtcConnectionState = ConnectionState.ERROR
+                self.__logger.warning('Could not send program!')
+                self.__connect()                
+        if not programSend:
+            self.__robotModel.rtcProgramRunning = False
+            self.__logger.error('Program re-sending timed out - Could not send program!')
+        time.sleep(0.1)
+
+
+    def __waitForProgram2Finish(self,prg):
+        '''
+        waiting for program to finish
+        '''
+        waitForProgramStart = len(prg)/50
+        notrun = 0
+        prgRest = 'def resetRegister():\n  write_output_boolean_register(0, False)\n  write_output_boolean_register(1, False)\nend\n'
+        while not self.__robotModel.stopRunningFlag and self.__robotModel.rtcProgramRunning:            
+            if self.__robotModel.SafetyStatus().StoppedDueToSafety:
+                self.__robotModel.rtcProgramRunning = False
+                self.__robotModel.rtcProgramExecutionError = True
+                self.__logger.error('SendProgram: Safety Stop')
+            elif self.__robotModel.OutputBitRegister()[0] == False:
+                self.__logger.debug('sendProgram: Program not started')
+                notrun += 1
+                if notrun > waitForProgramStart:
+                    self.__robotModel.rtcProgramRunning = False
+                    self.__logger.error('sendProgram: Program not able to run')
+            elif self.__robotModel.OutputBitRegister()[0] == True and self.__robotModel.OutputBitRegister()[1] == True:
+                self.__robotModel.rtcProgramRunning = False
+                self.__logger.info('sendProgram: Finished')
+            elif self.__robotModel.OutputBitRegister()[0] == True:
+                if self.__robotModel.RobotStatus().ProgramRunning:
+                    self.__logger.debug('sendProgram: UR running')
+                    notrun = 0
+                else:
+                    notrun += 1
+                    if notrun>10:
+                        self.__robotModel.rtcProgramRunning = False
+                        self.__robotModel.rtcProgramExecutionError = True
+                        self.__logger.error('SendProgram: Program Stopped but not finiched!!!')    
+            else:
+                self.__robotModel.rtcProgramRunning = False
+                self.__logger.error('SendProgram: Unknown error')
+            time.sleep(0.05)
+        self.__sendPrg(prgRest)
+        self.__robotModel.rtcProgramRunning = False
+        
\ No newline at end of file