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


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