Staubli_VAL3.py
Go to the documentation of this file.
1 # Copyright 2017 - RoboDK Software S.L. - http://www.robodk.com/
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at
5 # http://www.apache.org/licenses/LICENSE-2.0
6 # Unless required by applicable law or agreed to in writing, software
7 # distributed under the License is distributed on an "AS IS" BASIS,
8 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 # See the License for the specific language governing permissions and
10 # limitations under the License.
11 
12 # ----------------------------------------------------
13 # This file is a POST PROCESSOR for Robot Offline Programming to generate programs
14 # for a Staubli (VAL3) robot with RoboDK
15 #
16 # To edit/test this POST PROCESSOR script file:
17 # Select "Program"->"Add/Edit Post Processor", then select your post or create a new one.
18 # You can edit this file using any text editor or Python editor. Using a Python editor allows to quickly evaluate a sample program at the end of this file.
19 # Python should be automatically installed with RoboDK
20 #
21 # You can also edit the POST PROCESSOR manually:
22 # 1- Open the *.py file with Python IDLE (right click -> Edit with IDLE)
23 # 2- Make the necessary changes
24 # 3- Run the file to open Python Shell: Run -> Run module (F5 by default)
25 # 4- The "test_post()" function is called automatically
26 # Alternatively, you can edit this file using a text editor and run it with Python
27 #
28 # To use a POST PROCESSOR file you must place the *.py file in "C:/RoboDK/Posts/"
29 # To select one POST PROCESSOR for your robot in RoboDK you must follow these steps:
30 # 1- Open the robot panel (double click a robot)
31 # 2- Select "Parameters"
32 # 3- Select "Unlock advanced options"
33 # 4- Select your post as the file name in the "Robot brand" box
34 #
35 # To delete an existing POST PROCESSOR script, simply delete this file (.py file)
36 #
37 # ----------------------------------------------------
38 # More information about RoboDK Post Processors and Offline Programming here:
39 # http://www.robodk.com/help#PostProcessor
40 # http://www.robodk.com/doc/en/PythonAPI/postprocessor.html
41 # ----------------------------------------------------
42 
43 
44 # ----------------------------------------------------
45 # Import RoboDK tools
46 from robodk import *
47 from robolink import *
48 
49 
50 # Program.pjx file (references data file as %s.dtx)
51 PROGRAM_PJX = '''<?xml version="1.0" encoding="utf-8" ?>
52 <Project xmlns="http://www.staubli.com/robotics/VAL3/Project/3">
53  <Parameters version="s7.3.1" stackSize="5000" millimeterUnit="true" />
54  <Programs>
55  <Program file="start.pgx" />
56  <Program file="stop.pgx" />
57  </Programs>
58  <Database>
59  <Data file="%s.dtx" />
60  </Database>
61  <Libraries>
62  </Libraries>
63 </Project>
64 '''
65 
66 PROGRAM_PJX_MAIN = '''<?xml version="1.0" encoding="utf-8" ?>
67 <Project xmlns="http://www.staubli.com/robotics/VAL3/Project/3">
68  <Parameters version="s7.3.1" stackSize="5000" millimeterUnit="true" />
69  <Programs>
70  <Program file="loadNextOne.pgx" />
71  <Program file="start.pgx" />
72  <Program file="stop.pgx" />
73  </Programs>
74  <Database>
75  <Data file="%s.dtx" />
76  </Database>
77  <Libraries>
78  <Library alias="prog" path="./%s.pjx" />
79  <Library alias="prog_swap" path="./%s.pjx" />
80  <Library alias="tooldata" path="saveChangeTool" />
81  </Libraries>
82 </Project>'''
83 
84 DATA_DTX = '''<?xml version="1.0" encoding="utf-8" ?>
85 <Database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.staubli.com/robotics/VAL3/Data/2">
86  <Datas>
87  <Data name="%s" access="public" xsi:type="array" type="frame" size="%i">
88 %s </Data>
89  <Data name="jPark" access="public" xsi:type="array" type="jointRx" size="1">
90  <Value key="0" j1="0.000" j2="0.000" j3="90.000" j4="0.000" j5="90.000" j6="0.000" />
91  </Data>
92  <Data name="%s" access="public" xsi:type="array" type="jointRx" size="%i">
93 %s </Data>
94  <Data name="mNomSpeed" access="public" xsi:type="array" type="mdesc" size="1">
95  <Value key="0" accel="100" vel="100" decel="100" tmax="99999" rmax="99999" blend="off" leave="50" reach="50" />
96  </Data>
97  <Data name="%s" access="public" xsi:type="array" type="mdesc" size="%i">
98 %s </Data>
99  <Data name="nTraj" access="public" xsi:type="array" type="num" size="1"/>
100  <Data name="nTimeStop" access="private" xsi:type="array" type="num" size="1"/>
101  <Data name="nTimeStart" access="private" xsi:type="array" type="num" size="1"/>
102  <Data name="nMode" access="private" xsi:type="array" type="num" size="1"/>
103  <Data name="nEtat" access="private" xsi:type="array" type="num" size="1"/>
104  <Data name="%s" access="public" xsi:type="array" type="pointRx" size="%i">
105 %s </Data>
106  <Data name="%s" access="public" xsi:type="array" type="tool" size="%i">
107 %s </Data>
108  </Datas>
109 </Database>
110 '''
111 
112 DATA_DTX_MAIN = '''<?xml version="1.0" encoding="utf-8" ?>
113 <Database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.staubli.com/robotics/VAL3/Data/2">
114  <Datas>
115  <Data name="dioIN" access="public" xsi:type="array" type="dio" size="1">
116  <Value key="0" link="fIn0" />
117  </Data>
118  <Data name="dioOUT" access="public" xsi:type="array" type="dio" size="1">
119  <Value key="0" link="fOut0" />
120  </Data>
121  <Data name="dioRotationON" access="public" xsi:type="array" type="dio" size="1">
122  <Value key="0" link="BasicIO-1\\%%Q0" />
123  </Data>
124  <Data name="dioRotationOFF" access="public" xsi:type="array" type="dio" size="1">
125  <Value key="0" link="BasicIO-1\\%%Q1" />
126  </Data>
127  <Data name="dioBuse" access="public" xsi:type="array" type="dio" size="1">
128  <Value key="0" link="BasicIO-1\\%%Q2" />
129  </Data>
130  <Data name="fPartCad" access="public" xsi:type="array" type="frame" size="1">
131 %s </Data>
132  <Data name="fCadToReal" access="public" xsi:type="array" type="frame" size="1">
133  <Value key="0" x="0" y="0" z="0" rx="0" ry="0" rz="0" fatherId="fPartCad[0]" />
134  </Data>
135  <Data name="mNomSpeed" access="public" xsi:type="array" type="mdesc" size="1">
136  <Value key="0" accel="100" vel="100" decel="100" tmax="99999" rmax="99999" blend="off" leave="50" reach="50" />
137  </Data>
138  <Data name="nTraj" access="public" xsi:type="array" type="num" size="1"/>
139  <Data name="nTimeStop" access="private" xsi:type="array" type="num" size="1"/>
140  <Data name="nTimeStart" access="private" xsi:type="array" type="num" size="1"/>
141  <Data name="nMode" access="private" xsi:type="array" type="num" size="1"/>
142  <Data name="nEtat" access="private" xsi:type="array" type="num" size="1"/>
143  <Data name="tCad" access="public" xsi:type="array" type="tool" size="1">
144 %s </Data>
145  </Datas>
146 </Database>
147 '''
148 
149 # start.pjx file (references data file as %s.dtx)
150 START_PGX = '''<?xml version="1.0" encoding="utf-8" ?>
151 <Programs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.staubli.com/robotics/VAL3/Program/2">
152  <Program name="start" access="public">
153  <Code><![CDATA[
154 begin
155 %s
156 end
157  ]]></Code>
158  </Program>
159 </Programs>
160 '''
161 
162 
163 STOP_PGX = '''<?xml version="1.0" encoding="utf-8" ?>
164 <Programs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.staubli.com/robotics/VAL3/Program/2">
165  <Program name="stop" access="private">
166  <Code><![CDATA[
167  begin
168  resetMotion()
169  disablePower()
170  end
171  ]]></Code>
172  </Program>
173 </Programs>
174 '''
175 
176 LOAD_NEXT_ONE = '''<?xml version="1.0" encoding="utf-8" ?>
177 <Programs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.staubli.com/robotics/VAL3/Program/2">
178  <Program name="loadNextOne" access="private">
179  <Parameters xmlns="http://www.staubli.com/robotics/VAL3/Param/1">
180  <Parameter name="x_sName" type="string" xsi:type="element" />
181  </Parameters>
182  <Code><![CDATA[
183 begin
184  prog_swap:libLoad(x_sName)
185 end
186  ]]></Code>
187  </Program>
188 </Programs>'''
189 
190 
192  """Converts a pose to a Staubli target target"""
193  x = H[0,3]
194  y = H[1,3]
195  z = H[2,3]
196  a = H[0,0]
197  b = H[0,1]
198  c = H[0,2]
199  d = H[1,2]
200  e = H[2,2]
201  if c > (1.0 - 1e-10):
202  ry1 = pi/2
203  rx1 = 0
204  rz1 = atan2(H[1,0],H[1,1])
205  elif c < (-1.0 + 1e-10):
206  ry1 = -pi/2
207  rx1 = 0
208  rz1 = atan2(H[1,0],H[1,1])
209  else:
210  sy = c
211  cy1 = +sqrt(1-sy*sy)
212  sx1 = -d/cy1
213  cx1 = e/cy1
214  sz1 = -b/cy1
215  cz1 = a/cy1
216  rx1 = atan2(sx1,cx1)
217  ry1 = atan2(sy,cy1)
218  rz1 = atan2(sz1,cz1)
219  return [x, y, z, rx1*180.0/pi, ry1*180.0/pi, rz1*180.0/pi]
220 
221 # ----------------------------------------------------
222 def pose_2_str(pose):
223  """Prints a pose target"""
224  [x,y,z,r,p,w] = Pose_2_Staubli_v2(pose)
225  return ('x="%.3f" y="%.3f" z="%.3f" rx="%.3f" ry="%.3f" rz="%.3f"' % (x,y,z,r,p,w))
226 
227 def angles_2_str(angles):
228  """Prints a joint target for Staubli VAL3 XML"""
229  str = ''
230  for i in range(len(angles)):
231  str = str + ('j%i="%.5f" ' % ((i+1), angles[i]))
232  str = str[:-1]
233  return str
234 
235 def getSaveFolder(strdir='C:\\', strtitle='Save program folder ...'):
236  import tkinter
237  from tkinter import filedialog
238  options = {}
239  options['initialdir'] = strdir
240  options['title'] = strtitle
241  root = tkinter.Tk()
242  root.withdraw()
243  file_path = tkinter.filedialog.askdirectory(**options)
244  return file_path
245 
246 # ----------------------------------------------------
247 # Object class that handles the robot instructions/syntax
248 class RobotPost(object):
249  """Robot post object"""
250  # other variables
251  ROBOT_POST = ''
252  ROBOT_NAME = ''
253  PROG_FILES = []
254  PROG_NAME = 'unknown'
255  MAIN_FOLDER = 'ProgRoboDK'
256  PROG_PGX = ''
257  PROG_MOVE_COUNT = 0
258  PROG_MOVE_COUNT_MAX = 200
259  PROG_PGX_LIST = []
260  PROG_DTX_LIST = []
261  PROG_PJX_LIST = []
262  PROG_NAME_LIST = []
263  LOG = ''
264  nAxes = 6
265  TAB_PGX = ' '
266  DEFAULT_SPEED = 150
267  DEFAULT_SMOOTH = 0.1
268  SPEED = DEFAULT_SPEED
269  REF = eye(4)
270  TOOL = eye(4)
271  SMOOTH = DEFAULT_SMOOTH
272  REF_NAME = 'fPartReal'
273  REF_CURRENT = 'world[0]'
274  REF_DATA = ''
275  REF_COUNT = 0
276  TOOL_NAME = 'tCad'
277  TOOL_CURRENT = 'flange[0]'
278  TOOL_DATA = ''
279  TOOL_COUNT = 0
280  SPEED_NAME = 'mSpeed'
281  SPEED_CURRENT = 'mNomSpeed'
282  SPEED_DATA = ''
283  SPEED_COUNT = 0
284  JOINT_NAME = 'jJoint'
285  JOINT_DATA = ''
286  JOINT_COUNT = 0
287  POINT_NAME = 'pPoint'
288  POINT_DATA = ''
289  POINT_COUNT = 0
290 
291  def __init__(self, robotpost=None, robotname=None, robot_axes = 6, **kwargs):
292  self.ROBOT_POST = robotpost
293  self.ROBOT_NAME = robotname
294  self.PROG = ''
295  self.LOG = ''
296  self.nAxes = robot_axes
297 
298  def ProgStart(self, progname):
299  self.PROG_NAME = progname
300  self.addline('// Program %s start' % progname)
301 
302  def ProgFinish(self, progname):
303  self.addline('')
304  self.addline('waitEndMove()')
305  self.addline('// Program %s end' % progname)
306 
307 
308  def RemoveDirFTP(self, ftp, path):
309  import ftplib
310  """Recursively delete a directory tree on a remote server."""
311  wd = ftp.pwd()
312  try:
313  names = ftp.nlst(path)
314  except ftplib.all_errors as e:
315  # some FTP servers complain when you try and list non-existent paths
316  print('RemoveDirFTP: Could not remove {0}: {1}'.format(path, e))
317  return
318 
319  for name in names:
320  if os.path.split(name)[1] in ('.', '..'): continue
321  print('RemoveDirFTP: Checking {0}'.format(name))
322  try:
323  ftp.cwd(name) # if we can cwd to it, it's a folder
324  ftp.cwd(wd) # don't try a nuke a folder we're in
325  self.RemoveDirFTP(ftp, name)
326  except ftplib.all_errors:
327  ftp.delete(name)
328 
329  try:
330  ftp.rmd(path)
331  except ftplib.all_errors as e:
332  print('RemoveDirFTP: Could not remove {0}: {1}'.format(path, e))
333 
334  def UploadFTP(self, localpath):
335  import ftplib
336  import os
337  robot = None
338  try:
339  RDK = Robolink()
340  robot = RDK.Item(self.ROBOT_NAME, ITEM_TYPE_ROBOT)
341  [server_ip, port, remote_path, username, password] = robot.ConnectionParams()
342  except:
343  server_ip = 'localhost' # enter URL address of the robot it may look like: '192.168.1.123'
344  username = 'username' # enter FTP user name
345  password = 'password' # enter FTP password
346  remote_path = '/usr/usrapp/session/default/saveTraj/CAD' # enter the remote path
347  import sys
348  while True:
349  print("POPUP: Uploading program through FTP. Enter server parameters...")
350  sys.stdout.flush()
351 
352  # check if connection parameters are OK
353  values_ok = mbox('Using the following FTP connection parameters to transfer the program:\nRobot IP: %s\nRemote path: %s\nFTP username: %s\nFTP password: ****\n\nContinue?' % (server_ip, remote_path, username))
354  if values_ok:
355  print("Using default connection parameters")
356  else:
357  server_ip = mbox('Enter robot IP', entry=server_ip)
358  if not server_ip:
359  print('FTP upload cancelled by user')
360  return
361  remote_path = mbox('Enter the remote path (program folder) on the Staubli robot controller', entry=remote_path)
362  if not remote_path:
363  return
364  if remote_path.endswith('/'):
365  remote_path = remote_path[:-1]
366  rob_user_pass = mbox('Enter user name and password as\nuser-password', entry=('%s-%s' % (username, password)))
367  if not rob_user_pass:
368  return
369  name_value = rob_user_pass.split('-')
370  if len(name_value) < 2:
371  password = ''
372  else:
373  password = name_value[1]
374  username = name_value[0]
375  print("POPUP: <p>Connecting to <strong>%s</strong> using user name <strong>%s</strong> and password ****</p><p>Please wait...</p>" % (server_ip, username))
376  #print("POPUP: Trying to connect. Please wait...")
377  sys.stdout.flush()
378  if robot is not None:
379  robot.setConnectionParams(server_ip, port, remote_path, username, password)
380  pause(2)
381  try:
382  myFTP = ftplib.FTP(server_ip, username, password)
383  break;
384  except:
385  error_str = sys.exc_info()[1]
386  print("POPUP: Connection to %s failed: <p>%s</p>" % (server_ip,error_str))
387  sys.stdout.flush()
388  contin = mbox("Connection to %s failed. Reason:\n%s\n\nRetry?" % (server_ip,error_str))
389  if not contin:
390  return
391 
392  remote_path_prog = remote_path + '/' + self.MAIN_FOLDER
393  myPath = r'%s' % localpath
394  print("POPUP: Connected. Deleting existing files on %s..." % remote_path_prog)
395  sys.stdout.flush()
396  self.RemoveDirFTP(myFTP, remote_path_prog)
397  print("POPUP: Connected. Uploading program to %s..." % server_ip)
398  sys.stdout.flush()
399  try:
400  myFTP.cwd(remote_path)
401  myFTP.mkd(self.MAIN_FOLDER)
402  myFTP.cwd(remote_path_prog)
403  #print('asdf')
404  except:
405  error_str = sys.exc_info()[1]
406  print("POPUP: Remote path not found or can't be created: %s" % (remote_path))
407  sys.stdout.flush()
408  contin = mbox("Remote path\n%s\nnot found or can't create folder.\n\nChange path and permissions and retry." % remote_path)
409  return
410 
411  def uploadThis(path):
412  files = os.listdir(path)
413  os.chdir(path)
414  for f in files:
415  if os.path.isfile(path + r'\{}'.format(f)):
416  print(' Sending file: %s' % f)
417  print("POPUP: Sending file: %s" % f)
418  sys.stdout.flush()
419  fh = open(f, 'rb')
420  myFTP.storbinary('STOR %s' % f, fh)
421  fh.close()
422  elif os.path.isdir(path + r'\{}'.format(f)):
423  print(' Sending folder: %s' % f)
424  myFTP.mkd(f)
425  myFTP.cwd(f)
426  uploadThis(path + r'\{}'.format(f))
427  myFTP.cwd('..')
428  os.chdir('..')
429  uploadThis(myPath) # now call the recursive function
430 
431  def ProgSave(self, folder, progname, ask_user = False, show_result = False):
432  self.close_module()
433 
434  if ask_user or not DirExists(folder):
435  foldersave = getSaveFolder(folder, 'Save program as...')
436  if foldersave is not None and len(foldersave) > 0:
437  foldersave = foldersave
438  else:
439  return
440  else:
441  foldersave = folder
442 
443  nprogs = len(self.PROG_NAME_LIST)
444  print("Saving %i programs..." % nprogs)
445 
446  main_progname = 'Main' + progname
447  if True: #nprogs > 1: # always create a main program
448  folderprog = foldersave + '/' + main_progname
449  self.MAIN_FOLDER = main_progname
450  else:
451  folderprog = foldersave + '/' + progname
452  self.MAIN_FOLDER = progname
453 
454  if not DirExists(folderprog):
455  import os
456  os.makedirs(folderprog)
457 
458  show_file_list = []
459  if True: #nprogs > 1: # always create a main program
460  call_sequence = ''
461  for i in range(nprogs):
462  call_sequence+=(' if prog:libLoad("./%s")!=0\n' % self.PROG_NAME_LIST[i])
463  call_sequence+=(' logMsg("Error Loading RoboDK library")\n')
464  call_sequence+=(' popUpMsg("Error Loading RoboDK library")\n')
465  call_sequence+=(' endIf\n')
466  call_sequence+=(' wait(taskStatus("loading")==-1)\n')
467  if i < nprogs-1:
468  call_sequence+=(' taskCreate "loading",10,loadNextOne("./%s")\n' % self.PROG_NAME_LIST[i+1])
469  call_sequence+=(' prog:fPartReal.trsf=fPartCad.trsf*fCadToReal.trsf\n')
470  call_sequence+=(' prog:tCad.trsf=prog:tCad.trsf*{0,0,tooldata:nLength,0,0,0}\n')
471  call_sequence+=(' call prog:start()\n')
472  call_sequence+=(' \n')
473 
474  #-----------------------------------
475  # start.pgx
476  start_file = folderprog + '/start.pgx'
477  show_file_list.append(start_file)
478  fid = open(start_file, "w")
479  fid.write(START_PGX % call_sequence)
480  fid.close()
481  #-----------------------------------
482  # mainprog.pjx
483  project_file = folderprog + '/%s.pjx' % main_progname
484  #show_file_list.append(project_file)
485  fid = open(project_file, "w")
486  dummy_folder = self.PROG_NAME_LIST[0] + '/' + self.PROG_NAME_LIST[0]
487  fid.write(PROGRAM_PJX_MAIN % (main_progname, dummy_folder, dummy_folder))
488  fid.close()
489  print('SAVED: %s\n' % project_file)
490  #-----------------------------------
491  # mainprog.dtx
492  program_data = folderprog + '/%s.dtx' % main_progname
493  show_file_list.append(project_file)
494  fid = open(program_data, "w")
495  fid.write(DATA_DTX_MAIN % (self.REF_DATA, self.TOOL_DATA))
496  fid.close()
497  #-----------------------------------
498  # stop.pgx
499  stop_file = folderprog + '/stop.pgx'
500  fid = open(stop_file, "w")
501  fid.write(STOP_PGX)
502  fid.close()
503  #-----------------------------------
504  # loadNextOne.pgx
505  program_data = folderprog + '/loadNextOne.pgx'
506  fid = open(program_data, "w")
507  fid.write(LOAD_NEXT_ONE)
508  fid.close()
509  #-----------------------------------
510 
511  for i in range(nprogs):
512  if True: # nprogs > 1: # Always create a main program loading sub programs
513  folderprog_final = folderprog + '/' + self.PROG_NAME_LIST[i]
514  else:
515  folderprog_final = folderprog
516 
517  if not DirExists(folderprog_final):
518  import os
519  os.makedirs(folderprog_final)
520 
521  #-----------------------------------
522  # start.pgx
523  start_file = folderprog_final + '/start.pgx'
524  #show_file_list.append(start_file)
525  fid = open(start_file, "w")
526  fid.write(self.PROG_PGX_LIST[i])
527  fid.close()
528  #-----------------------------------
529  # stop.pgx
530  stop_file = folderprog_final + '/stop.pgx'
531  fid = open(stop_file, "w")
532  fid.write(STOP_PGX)
533  fid.close()
534  #-----------------------------------
535  # program.pjx
536  project_file = folderprog_final + '/%s.pjx' % self.PROG_NAME_LIST[i]
537  #show_file_list.append(project_file)
538  fid = open(project_file, "w")
539  fid.write(self.PROG_PJX_LIST[i])
540  fid.close()
541  print('SAVED: %s\n' % project_file)
542  #-----------------------------------
543  # program.dtx
544  program_data = folderprog_final + '/%s.dtx' % self.PROG_NAME_LIST[i]
545  #show_file_list.append(project_file)
546  fid = open(program_data, "w")
547  fid.write(self.PROG_DTX_LIST[i])
548  fid.close()
549  #-----------------------------------
550 
551  #self.UploadFTP(folderprog)
552  self.PROG_FILES = folderprog
553 
554  if show_result:
555  if type(show_result) is str:
556  # Open file with provided application
557  import subprocess
558  for file_i in show_file_list:
559  p = subprocess.Popen([show_result, file_i])
560  #p = subprocess.Popen([show_result, start_file])
561  #p = subprocess.Popen([show_result, program_data])
562  elif type(show_result) is list:
563  import subprocess
564  p = subprocess.Popen(show_result + [filesave])
565  else:
566  # open file with default application
567  import os
568  os.startfile(start_file)
569  os.startfile(program_data)
570  if len(self.LOG) > 0:
571  mbox('Program generation LOG:\n\n' + self.LOG)
572  # attempt FTP upload
573 
574  def ProgSendRobot(self, robot_ip, remote_path, ftp_user, ftp_pass):
575  """Send a program to the robot using the provided parameters. This method is executed right after ProgSave if we selected the option "Send Program to Robot".
576  The connection parameters must be provided in the robot connection menu of RoboDK"""
577  UploadFTP(self.PROG_FILES, robot_ip, remote_path, ftp_user, ftp_pass)
578 
579  def MoveJ(self, pose, joints, conf_RLF=None):
580  """Add a joint movement"""
581  self.control_ProgSize()
582  #nTraj=movej(jJoints[0],tTool[0],mSpeed[0])
583  #waitEndMove()
584  # <Value key="0" j1="0.000" j2="-10.000" j3="100.000" j4="0.000" j5="0.000" j6="-90.000" />
585  variable = '%s[%i]' % (self.JOINT_NAME, self.JOINT_COUNT)
586  self.JOINT_DATA = self.JOINT_DATA + ' <Value key="%i" %s />\n' % (self.JOINT_COUNT, angles_2_str(joints))
587  self.JOINT_COUNT = self.JOINT_COUNT + 1
588  self.addline('nTraj=movej(%s,%s,%s)' % (variable, self.TOOL_CURRENT, self.SPEED_CURRENT))
589  #self.addline('waitEndMove()')
590 
591  def MoveL(self, pose, joints, conf_RLF=None):
592  """Add a linear movement"""
593  self.control_ProgSize()
594  #nTraj=movej(jJoints[0],tTool[0],mSpeed[0])
595  #waitEndMove()
596  # <Value key="0" x="-36.802" y="-6.159" z="500.000" rx="135.407" ry="80.416" rz="46.453" shoulder="lefty" elbow="epositive" wrist="wpositive" fatherId="fPartReal[0]" />
597  # Configuration needs to be checked for older RoboDK versions
598  if conf_RLF == None:
599  str_config = 'shoulder="lefty" elbow="epositive" wrist="wpositive"'
600  else:
601  [rear, lowerarm, flip] = conf_RLF
602  str_config = 'shoulder="%s" elbow="%s" wrist="%s"' % ("righty" if rear>0 else "lefty", "enegative" if lowerarm>0 else "epositive", "wnegative" if flip>0 else "wpositive")
603  variable = '%s[%i]' % (self.POINT_NAME, self.POINT_COUNT)
604  self.POINT_DATA = self.POINT_DATA + ' <Value key="%i" %s %s fatherId="%s" />\n' % (self.POINT_COUNT, pose_2_str(pose), str_config, self.REF_CURRENT)
605  self.POINT_COUNT = self.POINT_COUNT + 1
606  self.addline('nTraj=movel(%s,%s,%s)' % (variable, self.TOOL_CURRENT, self.SPEED_CURRENT))
607 
608  def MoveC(self, pose1, joints1, pose2, joints2, conf_RLF_1=None, conf_RLF_2=None):
609  """Add a circular movement"""
610  self.control_ProgSize()
611  # Configuration needs to be checked for older RoboDK versions
612  if conf_RLF_1 == None:
613  str_config = 'shoulder="lefty" elbow="epositive" wrist="wpositive"'
614  else:
615  [rear, lowerarm, flip] = conf_RLF_1
616  str_config = 'shoulder="%s" elbow="%s" wrist="%s"' % ("righty" if rear>0 else "lefty", "enegative" if lowerarm>0 else "epositive", "wnegative" if flip>0 else "wpositive")
617  variable1 = '%s[%i]' % (self.POINT_NAME, self.POINT_COUNT)
618  variable2 = '%s[%i]' % (self.POINT_NAME, self.POINT_COUNT+1)
619  self.POINT_DATA = self.POINT_DATA + ' <Value key="%i" %s %s fatherId="%s" />\n' % (self.POINT_COUNT, pose_2_str(pose1), str_config, self.REF_CURRENT)
620  self.POINT_DATA = self.POINT_DATA + ' <Value key="%i" %s %s fatherId="%s" />\n' % (self.POINT_COUNT+1, pose_2_str(pose2), str_config, self.REF_CURRENT)
621  self.POINT_COUNT = self.POINT_COUNT + 2
622  self.addline('nTraj=movec(%s,%s,%s,%s)' % (variable1, variable2, self.TOOL_CURRENT, self.SPEED_CURRENT))
623 
624  def setFrame(self, pose, frame_id=None, frame_name=None):
625  """Change the robot reference frame"""
626  if self.REF_COUNT > 1:
627  self.RunMessage('Warning: This post processor is meant to use one reference frame. Errors might follow.', True)
628  self.log('This post processor is meant to use one reference frame. Errors might follow.')
629 
630  self.control_ProgSize()
631  self.REF = pose
632  # <Value key="0" x="600.000" y="0.000" z="-465.000" rx="0.400" ry="0.100" rz="-45.000" fatherId="world[0]" />
633  self.REF_CURRENT = '%s[%i]' % (self.REF_NAME, self.REF_COUNT)
634  self.REF_DATA = self.REF_DATA + ' <Value key="%i" %s fatherId="world[0]" />\n' % (self.REF_COUNT, pose_2_str(pose))
635  self.REF_COUNT = self.REF_COUNT + 1
636 
637  def setTool(self, pose, tool_id=None, tool_name=None):
638  """Change the robot TCP"""
639  if self.TOOL_COUNT > 1:
640  self.RunMessage('Warning: Only one tool allowed per program. Tool change skipped.', True)
641  self.log('Only one tool allowed per program')
642  return
643 
644  self.control_ProgSize()
645  self.TOOL = pose
646  # <Value key="0" x="-5.972" y="209.431" z="55.323" rx="-90.190" ry="-0.880" rz="89.997" fatherId="flange[0]" ioLink="valve1" />
647  self.TOOL_CURRENT = '%s[%i]' % (self.TOOL_NAME, self.TOOL_COUNT)
648  self.TOOL_DATA = self.TOOL_DATA + ' <Value key="%i" %s fatherId="flange[0]" ioLink="valve1" />\n' % (self.TOOL_COUNT, pose_2_str(pose))
649  self.TOOL_COUNT = self.TOOL_COUNT + 1
650 
651  def Pause(self, time_ms):
652  """Pause the robot program"""
653  self.control_ProgSize()
654  if time_ms < 0:
655  self.addline('popUpMsg("Paused. Press OK to continue")')
656  else:
657  self.addline('delay(%.3f)' % (time_ms*0.001))
658 
659  def setSpeed(self, speed_mms):
660  """Changes the robot speed (in mm/s)"""
661  # <Value key="0" accel="100" vel="100" decel="100" tmax="50" rmax="100" blend="joint" leave="0.1" reach="0.1" />
662  self.SPEED = speed_mms
663  self.SPEED_CURRENT = '%s[%i]' % (self.SPEED_NAME, self.SPEED_COUNT)
664  # blend = "off" / "joint" / "Cartesian"
665  #self.SPEED_DATA = self.SPEED_DATA + ' <Value key="%i" accel="100" vel="100" decel="100" tmax="%.1f" rmax="100" blend="cartesian" leave="%.1f" reach="%0.1f" />\n' % (self.SPEED_COUNT, speed_mms, self.SMOOTH, self.SMOOTH)
666  self.SPEED_DATA = self.SPEED_DATA + ' <Value key="%i" tmax="%.1f" rmax="100" leave="%.1f" reach="%0.1f" blend="cartesian" />\n' % (self.SPEED_COUNT, speed_mms, self.SMOOTH, self.SMOOTH)
667  self.SPEED_COUNT = self.SPEED_COUNT + 1
668 
669  def setAcceleration(self, accel_mmss):
670  """Changes the robot acceleration (in mm/s2)"""
671  self.ACCEL_MMSS = accel_mmss
672 
673  def setSpeedJoints(self, speed_degs):
674  """Changes the robot joint speed (in deg/s)"""
675  self.SPEED_DEGS = speed_degs
676 
677  def setAccelerationJoints(self, accel_degss):
678  """Changes the robot joint acceleration (in deg/s2)"""
679  self.ACCEL_DEGSS = accel_degss
680 
681  def setZoneData(self, zone_mm):
682  """Changes the zone data approach (makes the movement more smooth)"""
683  self.control_ProgSize()
684  self.SMOOTH = zone_mm
685 
686  def setDO(self, io_var, io_value):
687  """Sets a variable (output) to a given value"""
688  if type(io_var) != str: # set default variable name if io_var is a number
689  io_var = 'OUT[%s]' % str(io_var)
690  if type(io_value) != str: # set default variable value if io_value is a number
691  if io_value > 0:
692  io_value = 'TRUE'
693  else:
694  io_value = 'FALSE'
695 
696  # at this point, io_var and io_value must be string values
697  self.addline('%s=%s' % (io_var, io_value))
698 
699  def waitDI(self, io_var, io_value, timeout_ms=-1):
700  """Waits for an input io_var to attain a given value io_value. Optionally, a timeout can be provided."""
701  if type(io_var) != str: # set default variable name if io_var is a number
702  io_var = 'IN[%s]' % str(io_var)
703  if type(io_value) != str: # set default variable value if io_value is a number
704  if io_value > 0:
705  io_value = 'TRUE'
706  else:
707  io_value = 'FALSE'
708 
709  # at this point, io_var and io_value must be string values
710  if timeout_ms < 0:
711  self.addline('WAIT FOR %s==%s' % (io_var, io_value))
712  else:
713  self.addline('WAIT FOR %s==%s TIMEOUT=%.1f' % (io_var, io_value, timeout_ms))
714 
715  def RunCode(self, code, is_function_call = False):
716  """Adds code or a function call"""
717  self.control_ProgSize()
718  if is_function_call:
719  code.replace(' ','_')
720  if not code.endswith(')'):
721  code = code + '()'
722  #call prog:start()
723  #self.addline('call prog:%s' % code)# add as a call
724  self.addline('//call prog:%s' % code)# add as a comment
725  else:
726  self.addline(code)
727 
728  def RunMessage(self, message, iscomment = False):
729  """Display a message in the robot controller screen (teach pendant)"""
730  self.control_ProgSize()
731  if iscomment:
732  self.addline('// ' + message)
733  else:
734  self.addline('popUpMsg("%s")' % message)
735 
736 # ------------------ private ----------------------
737  def addline(self, newline):
738  """Add a program line"""
739  self.PROG_PGX = self.PROG_PGX + self.TAB_PGX + newline + '\n'
740 
741  def addlog(self, newline):
742  """Add a log message"""
743  self.LOG = self.LOG + newline + '\n'
744 
745  def control_ProgSize(self):
747  if self.PROG_MOVE_COUNT > self.PROG_MOVE_COUNT_MAX:
748  self.close_module()
749 
750  def close_module(self):
751  if self.PROG_MOVE_COUNT == 0:
752  return
753 
754  progname = self.PROG_NAME
755  nprogs = len(self.PROG_NAME_LIST)
756  if nprogs > 0:
757  progname = progname + ('%i' % (nprogs+1))
758 
759  self.PROG_PGX_LIST.append(START_PGX % self.PROG_PGX)
760  self.PROG_DTX_LIST.append(DATA_DTX % (self.REF_NAME, self.REF_COUNT, self.REF_DATA, self.JOINT_NAME, self.JOINT_COUNT, self.JOINT_DATA, self.SPEED_NAME, self.SPEED_COUNT, self.SPEED_DATA, self.POINT_NAME, self.POINT_COUNT, self.POINT_DATA, self.TOOL_NAME, self.TOOL_COUNT, self.TOOL_DATA))
761  self.PROG_PJX_LIST.append(PROGRAM_PJX % progname)
762  self.PROG_NAME_LIST.append(progname)
763  self.PROG_PGX = ''
764  self.REF_DATA = ''
765  self.REF_COUNT = 0
766  self.TOOL_DATA = ''
767  self.TOOL_COUNT = 0
768  self.SPEED_DATA = ''
769  self.SPEED_COUNT = 0
770  self.JOINT_DATA = ''
771  self.JOINT_COUNT = 0
772  self.POINT_DATA = ''
773  self.POINT_COUNT = 0
774  self.PROG_MOVE_COUNT = 0
775  # initialise next program
776  self.setFrame(self.REF)
777  self.setTool(self.TOOL)
778  self.setSpeed(self.SPEED)
779  self.PROG_MOVE_COUNT = 0 # very important to avoid writting two programs
780 
781 
782 
783 
784 
785 
786 # -------------------------------------------------
787 # ------------ For testing purposes ---------------
788 def Pose(xyzrpw):
789  [x,y,z,r,p,w] = xyzrpw
790  a = r*math.pi/180
791  b = p*math.pi/180
792  c = w*math.pi/180
793  ca = math.cos(a)
794  sa = math.sin(a)
795  cb = math.cos(b)
796  sb = math.sin(b)
797  cc = math.cos(c)
798  sc = math.sin(c)
799  return Mat([[cb*ca, ca*sc*sb - cc*sa, sc*sa + cc*ca*sb, x],[cb*sa, cc*ca + sc*sb*sa, cc*sb*sa - ca*sc, y],[-sb, cb*sc, cc*cb, z],[0,0,0,1]])
800 
801 def test_post():
802  """Test the post with a basic program"""
803 
804  robot = RobotPost()
805 
806  robot.ProgStart("Program")
807  robot.RunMessage("Program generated by RoboDK using a custom post processor", True)
808  robot.setFrame(Pose([807.766544, -963.699898, 41.478944, 0, 0, 0]))
809  robot.setTool(Pose([62.5, -108.253175, 100, -60, 90, 0]))
810  robot.MoveJ(Pose([200, 200, 500, 180, 0, 180]), [-46.18419, -6.77518, -20.54925, 71.38674, 49.58727, -302.54752] )
811  robot.MoveL(Pose([200, 250, 348.734575, 180, 0, -150]), [-41.62707, -8.89064, -30.01809, 60.62329, 49.66749, -258.98418] )
812  robot.MoveL(Pose([200, 200, 262.132034, 180, 0, -150]), [-43.73892, -3.91728, -35.77935, 58.57566, 54.11615, -253.81122] )
813  robot.RunMessage("Setting air valve 1 on")
814  robot.RunCode("TCP_On", True)
815  robot.Pause(1000)
816  robot.MoveL(Pose([200, 250, 348.734575, 180, 0, -150]), [-41.62707, -8.89064, -30.01809, 60.62329, 49.66749, -258.98418] )
817  robot.MoveL(Pose([250, 300, 278.023897, 180, 0, -150]), [-37.52588, -6.32628, -34.59693, 53.52525, 49.24426, -251.44677] )
818  robot.MoveL(Pose([250, 250, 191.421356, 180, 0, -150]), [-39.75778, -1.04537, -40.37883, 52.09118, 54.15317, -246.94403] )
819  robot.RunMessage("Setting air valve off")
820  robot.RunCode("TCP_Off", True)
821  robot.Pause(1000)
822  robot.MoveL(Pose([250, 300, 278.023897, 180, 0, -150]), [-37.52588, -6.32628, -34.59693, 53.52525, 49.24426, -251.44677] )
823  robot.MoveL(Pose([250, 200, 278.023897, 180, 0, -150]), [-41.85389, -1.95619, -34.89154, 57.43912, 52.34162, -253.73403] )
824  robot.MoveL(Pose([250, 150, 191.421356, 180, 0, -150]), [-43.82111, 3.29703, -40.29493, 56.02402, 56.61169, -249.23532] )
825  robot.ProgFinish("Program")
826  # robot.ProgSave(".","Program",True)
827  print(robot.PROG_PGX)
828  if len(robot.LOG) > 0:
829  mbox('Program generation LOG:\n\n' + robot.LOG)
830 
831  input("Press Enter to close...")
832 
833 if __name__ == "__main__":
834  """Function to call when the module is executed by itself: test"""
835  test_post()
def setTool(self, pose, tool_id=None, tool_name=None)
def RunCode(self, code, is_function_call=False)
def MoveC(self, pose1, joints1, pose2, joints2, conf_RLF_1=None, conf_RLF_2=None)
def MoveJ(self, pose, joints, conf_RLF=None)
def ProgSave(self, folder, progname, ask_user=False, show_result=False)
def __init__(self, robotpost=None, robotname=None, robot_axes=6, kwargs)
def MoveL(self, pose, joints, conf_RLF=None)
def mbox(msg, b1='OK', b2='Cancel', frame=True, t=False, entry=None)
Definition: robodk.py:1458
def ProgSendRobot(self, robot_ip, remote_path, ftp_user, ftp_pass)
def waitDI(self, io_var, io_value, timeout_ms=-1)
def getSaveFolder(strdir='C:\\', strtitle='Save program folder ...')
def setFrame(self, pose, frame_id=None, frame_name=None)
def RunMessage(self, message, iscomment=False)


ros_robodk_post_processors
Author(s): Victor Lamoine - Institut Maupertuis
autogenerated on Sun Jun 7 2020 03:50:22