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

Switch to unified view

a b/URBasic/realTimeClient.py
1
'''
2
Python 3.x library to control an UR robot through its TCP/IP interfaces
3
Copyright (C) 2017  Martin Huus Bjerge, Rope Robotics ApS, Denmark
4
5
Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
6
and associated documentation files (the "Software"), to deal in the Software without restriction, 
7
including without limitation the rights to use, copy, modify, merge, publish, distribute, 
8
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 
9
is furnished to do so, subject to the following conditions:
10
11
The above copyright notice and this permission notice shall be included in all copies 
12
or substantial portions of the Software.
13
14
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
15
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
16
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL "Rope Robotics ApS" BE LIABLE FOR ANY CLAIM, 
17
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
18
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20
Except as contained in this notice, the name of "Rope Robotics ApS" shall not be used 
21
in advertising or otherwise to promote the sale, use or other dealings in this Software 
22
without prior written authorization from "Rope Robotics ApS".
23
'''
24
__author__ = "Martin Huus Bjerge"
25
__copyright__ = "Copyright 2017, Rope Robotics ApS, Denmark"
26
__license__ = "MIT License"
27
28
import URBasic
29
import socket
30
import threading
31
import select
32
import re
33
import numpy as np
34
import time
35
36
DEFAULT_TIMEOUT = 1.0
37
38
class ConnectionState:
39
    ERROR = 0
40
    DISCONNECTED = 1
41
    CONNECTED = 2
42
    PAUSED = 3
43
    STARTED = 4
44
45
class RealTimeClient(object):
46
    '''
47
    Interface to UR robot Real Time Client interface.
48
    For more detailes see this site:
49
    http://www.universal-robots.com/how-tos-and-faqs/how-to/ur-how-tos/remote-control-via-tcpip-16496/
50
    
51
    The Real Time Client in this version is only used to send program and script commands 
52
    to the robot, not to read data from the robot, all data reading is done via the RTDE interface.
53
    
54
    The constructor takes a UR robot hostname as input, and a RTDE configuration file.
55
56
    Input parameters:
57
    host (string):  hostname or IP of UR Robot (RT CLient server)
58
    conf_filename (string):  Path to xml file describing what channels to activate
59
    logger (URBasis_DataLogging obj): A instance if a logger object if common logging is needed.
60
61
    
62
    Example:
63
    rob = URBasic.realTimeClient.RT_CLient('192.168.56.101')
64
    self.close_rtc()
65
    '''
66
67
68
    def __init__(self, robotModel):
69
        '''
70
        Constructor see class description for more info.
71
        '''
72
        if(False):
73
            assert isinstance(robotModel, URBasic.robotModel.RobotModel)  ### This line is to get code completion for RobotModel
74
        self.__robotModel = robotModel
75
76
        logger = URBasic.dataLogging.DataLogging()
77
        name = logger.AddEventLogging(__name__, log2Consol=False,level = URBasic.logging.WARNING)
78
        self.__logger = logger.__dict__[name]
79
        self.__robotModel.rtcConnectionState = ConnectionState.DISCONNECTED
80
        self.__reconnectTimeout = 60
81
        self.__sock = None
82
        self.__thread = None
83
        if self.__connect():
84
            self.__logger.info('RT_CLient constructor done')
85
        else:
86
            self.__logger.info('RT_CLient constructor done but not connected')
87
        
88
    def __connect(self):
89
        '''
90
        Initialize RT Client connection to host .
91
        
92
        Return value:
93
        success (boolean)
94
        
95
        Example:
96
        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101')
97
        rob.connect()
98
        '''       
99
        if self.__sock:
100
            return True
101
102
        t0 = time.time()
103
        while (time.time()-t0<self.__reconnectTimeout) and self.__robotModel.rtcConnectionState < ConnectionState.CONNECTED:
104
            try:
105
                self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)            
106
                self.__sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)         
107
                self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
108
                self.__sock.settimeout(DEFAULT_TIMEOUT)
109
                self.__sock.connect((self.__robotModel.ipAddress, 30003))
110
                self.__robotModel.rtcConnectionState = ConnectionState.CONNECTED
111
                time.sleep(0.5)
112
                self.__logger.info('Connected')
113
                return True
114
            except (socket.timeout, socket.error):
115
                self.__sock = None
116
                self.__logger.error('RTC connecting')
117
118
        return False
119
                
120
121
    def Disconnect(self):
122
        '''
123
        Disconnect the RT Client connection.
124
        '''        
125
        if self.__sock:
126
            self.__sock.close()
127
            self.__sock = None
128
            self.__logger.info('Disconnected')
129
        self.__robotModel.rtcConnectionState = ConnectionState.DISCONNECTED
130
        return True
131
132
133
    def IsRtcConnected(self):
134
        '''
135
        Returns True if the connection is open.
136
137
        Return value:
138
        status (boolean): True if connected and False of not connected.
139
140
        Example:
141
        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101')
142
        rob.connect()
143
        print(rob.is_connected())
144
        rob.disconnect()
145
        '''
146
        return self.__robotModel.rtcConnectionState > ConnectionState.DISCONNECTED
147
        
148
    def SendProgram(self,prg=''):
149
        '''
150
        Send a new command or program (string) to the UR controller. 
151
        The command or program will be executed as soon as it's received by the UR controller. 
152
        Sending a new command or program while stop and existing running command or program and start the new one.
153
        The program or command will also bee modified to include some control signals to be used
154
        for monitoring if a program execution is successful and finished.  
155
156
        Input parameters:
157
        prg (string): A string containing a single command or a whole program.
158
159
        Example:
160
        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101',logger=logger)
161
        rob.connect()
162
        rob.send_srt('set_digital_out(0, True)')
163
        rob.disconnect()        
164
        '''
165
        if not self.IsRtcConnected():
166
            if not self.__connect():
167
                self.__logger.error('SendProgram: Not connected to robot')
168
 
169
        if self.__robotModel.stopRunningFlag:
170
            self.__logger.info('SendProgram: Send program aborted due to stopRunningFlag')
171
            return
172
 
173
        #Close down previous thread 
174
        if self.__thread is not None:
175
            if self.__robotModel.rtcProgramRunning:
176
                self.__robotModel.stopRunningFlag = True
177
                while self.__robotModel.rtcProgramRunning: time.sleep(0.1)
178
                self.__robotModel.stopRunningFlag = False
179
            self.__thread.join()
180
            
181
        
182
        #Rest status bits
183
        self.__robotModel.rtcProgramRunning = True
184
        self.__robotModel.rtcProgramExecutionError = False
185
        
186
        #Send and wait from program
187
        self.__sendPrg(self.__AddStatusBit2Prog(prg))        
188
        self.__thread = threading.Thread(target=self.__waitForProgram2Finish, kwargs={'prg': prg})
189
        self.__thread.start()
190
        #self.__waitForProgram2Finish(prg)
191
            
192
    def Send(self,prg=''):
193
        '''
194
        Send a new command (string) to the UR controller. 
195
        The command or program will be executed as soon as it's received by the UR controller. 
196
        Sending a new command or program while stop and existing running command or program and start the new one.
197
        The program or command will also bee modified to include some control signals to be used
198
        for monitoring if a program execution is successful and finished.  
199
200
        Input parameters:
201
        prg (string): A string containing a single command or a whole program.
202
203
204
        Example:
205
        rob = URBasic.realTimeClient.RT_CLient('192.168.56.101',logger=logger)
206
        rob.connect()
207
        rob.send_srt('set_digital_out(0, True)')
208
        rob.disconnect()        
209
        '''
210
        if not self.IsRtcConnected():
211
            if not self.__connect():
212
                self.__logger.error('SendProgram: Not connected to robot')
213
        if self.__robotModel.stopRunningFlag:
214
            self.__logger.info('SendProgram: Send command aborted due to stopRunningFlag')
215
            return
216
    
217
        #Rest status bits
218
        self.__robotModel.rtcProgramRunning = True
219
        self.__robotModel.rtcProgramExecutionError = False
220
        
221
        #Send
222
        self.__sendPrg(prg)      
223
        self.__robotModel.rtcProgramRunning = False
224
225
    def __AddStatusBit2Prog(self,prg):
226
        '''
227
        Modifying program to include status bit's in beginning and end of program
228
        '''
229
        def1 = prg.find('def ')
230
        if def1>=0:
231
            prglen = len(prg)
232
            prg = prg.replace('):\n', '):\n  write_output_boolean_register(0, True)\n',1)
233
            if len(prg) == prglen:
234
                self.__logger.warning('Send_program: Syntax error in program')
235
                return False
236
                
237
            if (len(re.findall('def ', prg)))>1:
238
                mainprg = prg[0:prg[def1+4:].find('def ')+def1+4]
239
                mainPrgEnd = (np.max([mainprg.rfind('end '), mainprg.rfind('end\n')]))
240
                prg = prg.replace(prg[0:mainPrgEnd], prg[0:mainPrgEnd] + '\n  write_output_boolean_register(1, True)\n',1)
241
            else:
242
                mainPrgEnd = prg.rfind('end')
243
                prg = prg.replace(prg[0:mainPrgEnd], prg[0:mainPrgEnd] + '\n  write_output_boolean_register(1, True)\n',1)
244
                
245
        else:
246
            prg = 'def script():\n  write_output_boolean_register(0, True)\n  ' + prg + '\n  write_output_boolean_register(1, True)\nend\n'
247
        return prg
248
        
249
    def __sendPrg(self,prg):
250
        '''
251
        Sending program str via socket
252
        '''
253
        programSend = False      
254
        self.__robotModel.forceRemoteActiveFlag = False
255
        while not self.__robotModel.stopRunningFlag and not programSend:
256
            try:
257
                (_, writable, _) = select.select([], [self.__sock], [], DEFAULT_TIMEOUT)
258
                if len(writable):
259
                    self.__sock.send(prg.encode())
260
                    self.__logger.info('Program send to Robot:\n' + prg)
261
                    programSend = True
262
            except:
263
                self.__sock = None
264
                self.__robotModel.rtcConnectionState = ConnectionState.ERROR
265
                self.__logger.warning('Could not send program!')
266
                self.__connect()                
267
        if not programSend:
268
            self.__robotModel.rtcProgramRunning = False
269
            self.__logger.error('Program re-sending timed out - Could not send program!')
270
        time.sleep(0.1)
271
272
273
    def __waitForProgram2Finish(self,prg):
274
        '''
275
        waiting for program to finish
276
        '''
277
        waitForProgramStart = len(prg)/50
278
        notrun = 0
279
        prgRest = 'def resetRegister():\n  write_output_boolean_register(0, False)\n  write_output_boolean_register(1, False)\nend\n'
280
        while not self.__robotModel.stopRunningFlag and self.__robotModel.rtcProgramRunning:            
281
            if self.__robotModel.SafetyStatus().StoppedDueToSafety:
282
                self.__robotModel.rtcProgramRunning = False
283
                self.__robotModel.rtcProgramExecutionError = True
284
                self.__logger.error('SendProgram: Safety Stop')
285
            elif self.__robotModel.OutputBitRegister()[0] == False:
286
                self.__logger.debug('sendProgram: Program not started')
287
                notrun += 1
288
                if notrun > waitForProgramStart:
289
                    self.__robotModel.rtcProgramRunning = False
290
                    self.__logger.error('sendProgram: Program not able to run')
291
            elif self.__robotModel.OutputBitRegister()[0] == True and self.__robotModel.OutputBitRegister()[1] == True:
292
                self.__robotModel.rtcProgramRunning = False
293
                self.__logger.info('sendProgram: Finished')
294
            elif self.__robotModel.OutputBitRegister()[0] == True:
295
                if self.__robotModel.RobotStatus().ProgramRunning:
296
                    self.__logger.debug('sendProgram: UR running')
297
                    notrun = 0
298
                else:
299
                    notrun += 1
300
                    if notrun>10:
301
                        self.__robotModel.rtcProgramRunning = False
302
                        self.__robotModel.rtcProgramExecutionError = True
303
                        self.__logger.error('SendProgram: Program Stopped but not finiched!!!')    
304
            else:
305
                self.__robotModel.rtcProgramRunning = False
306
                self.__logger.error('SendProgram: Unknown error')
307
            time.sleep(0.05)
308
        self.__sendPrg(prgRest)
309
        self.__robotModel.rtcProgramRunning = False
310