Hekateros  3.4.3
RoadNarrows Robotics Robot Arm Project
dynacfg.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 
3 ###############################################################################
4 #
5 # Package: Hekateros
6 #
7 # File: dynacfg.py
8 #
9 ## \file
10 ##
11 ## $LastChangedDate: 2014-09-18 16:53:49 -0600 (Thu, 18 Sep 2014) $
12 ## $Rev: 3748 $
13 ##
14 ## \brief Configure Hekateros servo.
15 ##
16 ## \author Robin Knight (robin.knight@roadnarrows.com)
17 ##
18 ## \copyright
19 ## \h_copy 2013-2017. RoadNarrows LLC.\n
20 ## http://www.roadnarrows.com\n
21 ## All Rights Reserved
22 ##
23 # @EulaBegin@
24 #
25 # Unless otherwise stated explicitly, all materials contained are copyrighted
26 # and may not be used without RoadNarrows LLC's written consent,
27 # except as provided in these terms and conditions or in the copyright
28 # notice (documents and software) or other proprietary notice provided with
29 # the relevant materials.
30 #
31 # IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
32 # MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
33 # BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
34 # CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
35 # DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
36 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #
38 # THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
39 # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
40 # FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
41 # "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
42 # PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
43 #
44 # @EulaEnd@
45 #
46 ###############################################################################
47 
48 import sys
49 import os
50 import time
51 import getopt
52 
53 from Tkinter import *
54 from Tkconstants import *
55 from tkFileDialog import *
56 import tkFont
57 
58 from PIL import Image, ImageTk
59 
60 # ------------------------------------------------------------------------------
61 # Globals
62 # ------------------------------------------------------------------------------
63 
64 ## \brief Application version. Update as needed.
65 appVersion = '1.0.0'
66 
67 ## \brief Image search paths.
68 imagePath = [
69  "/prj/pkg/Hekateros/share/images",
70  "/usr/local/share/Hekateros/images",
71  "/prj/pkg/appkit/share/images",
72  "/usr/local/share/appkit/images"
73 ]
74 
75 
76 hekScriptPath = "/prj/pkg/Hekateros/docs/factory/dynamixel"
77 
78 hekProducts = ['4S', '4L', '5S', '5L']
79 
80 hekDynaScripts = {
81  '4S':
82  { 'base_rot': None,
83  'shoulder_m': 'shoulder_m.scr',
84  'shoulder_s': 'shoulder_s.scr',
85  'elbow': 'elbow.scr',
86  'wrist_pitch': 'wrist_pitch.scr',
87  'wrist_rot': 'wrist_rot.scr',
88  'gripper': 'gripper.scr'
89  },
90  '4L':
91  { 'base_rot': None,
92  'shoulder_m': 'shoulder_m.scr',
93  'shoulder_s': 'shoulder_s.scr',
94  'elbow': 'elbow.scr',
95  'wrist_pitch': 'wrist_pitch.scr',
96  'wrist_rot': 'wrist_rot.scr',
97  'gripper': 'gripper.scr'
98  },
99  '5S':
100  { 'base_rot': 'base_rot.scr',
101  'shoulder_m': 'shoulder_m.scr',
102  'shoulder_s': 'shoulder_s.scr',
103  'elbow': 'elbow.scr',
104  'wrist_pitch': 'wrist_pitch.scr',
105  'wrist_rot': 'wrist_rot.scr',
106  'gripper': 'gripper.scr'
107  },
108  '5L':
109  { 'base_rot': 'base_rot.scr',
110  'shoulder_m': 'shoulder_m.scr',
111  'shoulder_s': 'shoulder_s.scr',
112  'elbow': 'elbow.scr',
113  'wrist_pitch': 'wrist_pitch.scr',
114  'wrist_rot': 'wrist_rot.scr',
115  'gripper': 'gripper.scr'
116  }
117 }
118 
119 hekBaudRates = [9600, 19200, 57600, 115200, 200000, 400000, 500000, 1000000]
120 
121 ## \brief Common foreground colors.
122 fgColors = {
123  'normal': 'black',
124  'ok': '#008800',
125  'focus': '#0000aa',
126  'warning': '#aa6600',
127  'error': '#cc0000'
128 }
129 
130 # ------------------------------------------------------------------------------
131 # Utilities
132 # ------------------------------------------------------------------------------
133 
134 #
135 ## \brief Load icon image from file name.
136 ##
137 ## \param filename Icon file name.
138 ##
139 ## \return Returns icon widget on success, None on failure.
140 #
141 def loadIcon(filename):
142  # no file name
143  if filename is None or len(filename) == 0:
144  return None;
145  # absolute file name
146  if filename[0] == os.path.sep:
147  try:
148  return ImageTk.PhotoImage(Image.open(filename))
149  except IOError:
150  return None
151  # relative file name - search path for file
152  for path in imagePath:
153  fqname = path + os.path.sep + filename
154  try:
155  return ImageTk.PhotoImage(Image.open(fqname))
156  except IOError:
157  continue
158  return None
159 
160 #
161 ## Round to nearest 100th.
162 #
163 def round100th(x):
164  return math.floor((x + 0.005) * 100.0) / 100.0
165 
166 #
167 ## Round to nearest 10th.
168 #
169 def round10th(x):
170  return math.floor((x + 0.05) * 10.0) / 10.0
171 
172 #
173 ## Degrees to radians.
174 #
175 def degToRad(deg):
176  return deg / 180.0 * math.pi
177 
178 #
179 ## Radians to degrees.
180 #
181 def radToDeg(rad):
182  return rad / math.pi * 180.0
183 
184 
185 
186 # ------------------------------------------------------------------------------
187 # Class window
188 # ------------------------------------------------------------------------------
189 
190 ##
191 ## \brief Window class supporting application.
192 ##
193 class window(Frame):
194  #
195  ## \brief Constructor.
196  ##
197  ## \param master Window parent master widget.
198  ## \param cnf Configuration dictionary.
199  ## \param kw Keyword options.
200  #
201  def __init__(self, master=None, cnf={}, **kw):
202  # intialize window data
203  kw = self.initData(kw)
204 
205  Frame.__init__(self, master=master, cnf=cnf, **kw)
206  self.master.title("Hekateros Factory Servo Configuration")
207  self.grid(row=0, column=0, padx=5, pady=5)
208 
209  # craete and show widgets
210  self.createWidgets()
211 
212  #
213  ## \brief Initialize class state data.
214  ##
215  ## Any keywords for this application specific window that are not supported
216  ## by the Frame Tkinter class must be removed.
217  ##
218  ## \param kw Keyword options.
219  ##
220  ## \return Modified keywords sans this specific class.
221  #
222  def initData(self, kw):
223  self.m_icons = {}
224  self.m_debug = False;
225  self.m_uri = '/dev/ttyUSB0'
226  self.m_baudrate = 57600
227  self.m_product = None
228  self.m_joint = None
229 
230  if kw.has_key('debug'):
231  self.m_debug = kw['debug']
232  del kw['debug']
233  if kw.has_key('arm'):
234  self.m_product = kw['arm']
235  del kw['arm']
236  if kw.has_key('joint'):
237  self.m_joint = kw['joint']
238  del kw['joint']
239  if kw.has_key('uri'):
240  self.m_uri = kw['uri']
241  del kw['uri']
242  if kw.has_key('baudrate'):
243  self.m_baudrate = kw['baudrate']
244  del kw['baudrate']
245 
246  return kw
247 
248  #
249  ## \brief Create gui widgets with supporting data and show.
250  #
251  def createWidgets(self):
252  self.createHeading(self, 0, 0)
253  self.createCfgPanel(self, 1, 0)
254  self.createStatusBar(self, 2, 0)
255 
256  #
257  ## \brief Create top gui heading.
258  #
259  def createHeading(self, parent, row, col):
260  wframe = Frame(parent)
261  wframe['borderwidth'] = 2
262  wframe['relief'] = 'ridge'
263  wframe.grid(row=row, column=col, padx=1, pady=3, sticky=N+W+E)
264  self.m_wJointStateFrame = wframe
265 
266  # rn logo
267  w = Label(wframe)
268  self.m_icons['rn_logo'] = loadIcon("RNLogo48.png");
269  if self.m_icons['rn_logo']:
270  w['image'] = self.m_icons['rn_logo']
271  else:
272  w['text'] = 'rn'
273  w['anchor'] = W
274  w['width'] = 5
275  w.grid(row=0, column=0, sticky=W)
276 
277  # top heading
278  w = Label(wframe)
279  w['font'] = ('Helvetica', 16)
280  w['text'] = 'Hekateros Factory Servo Configuration'
281  w['anchor'] = CENTER
282  w['width'] = 40
283  w.grid(row=0, column=1, sticky=E+W)
284 
285  # hek logo
286  w = Label(wframe)
287  self.m_icons['hek_logo'] = loadIcon("icons/icon_hek_logo.png");
288  if self.m_icons['hek_logo']:
289  w['image'] = self.m_icons['hek_logo']
290  w['anchor'] = E
291  else:
292  w['text'] = 'hek'
293  w['anchor'] = E
294  w['width'] = 5
295  w.grid(row=0, column=2, sticky=E)
296 
297  #
298  ## \brief Create joint state lower center panel headers.
299  ##
300  ## \param parent Parent widget
301  #
302  def createCfgPanel(self, parent, row, col):
303  wframe = Frame(parent)
304  wframe['borderwidth'] = 2
305  wframe['relief'] = 'ridge'
306  wframe.grid(row=row, column=col, padx=1, pady=3, sticky=N+W+E)
307  self.m_wJointStateFrame = wframe
308 
309  # heading
310  w = Label(wframe)
311  w['font'] =('Helvetica', 12)
312  w['text'] = 'Servo Configuration'
313  w['anchor'] = CENTER
314  w.grid(row=0, column=0, columnspan=10, sticky=E+W)
315 
316  width = 12
317  padx = 8
318  pady = 3
319  row = 1
320  col = 0
321 
322  # column labels, line 1
323  for text in [' ', 'Target', 'Current', 'Current', ' ']:
324  w = Label(wframe, width=width, padx=padx, pady=pady, anchor=CENTER,
325  text=text)
326  w.grid(row=row, column=col, pady=0, sticky=W+E)
327  col += 1
328 
329  row += 1
330  col = 0
331 
332  # column labels, line 2
333  for text in ['Joint Servo', 'Servo Id', 'Servo Id', 'Baudrate',
334  'Configure']:
335  w = Label(wframe, width=width, padx=padx, pady=0, anchor=CENTER,
336  text=text)
337  w.grid(row=row, column=col, sticky=W+E)
338  col += 1
339 
340  #
341  ## \brief Create gui status bar at bottom of gui window.
342  #
343  def createStatusBar(self, parent, row, col):
344  wframe = Frame(self)
345  wframe['borderwidth'] = 2
346  wframe['relief'] = 'ridge'
347  wframe.grid(row=2, column=0, columnspan=3, padx=1, pady=3, sticky=N+E+W+S)
348 
349  self.m_varStatus = StringVar()
350  self.m_varStatus.set("Calibration required.")
351  self.m_wStatusBar = Entry(wframe)
352  self.m_wStatusBar['width'] = wframe['width']
353  self.m_wStatusBar['relief'] = 'flat'
354  self.m_wStatusBar['textvar'] = self.m_varStatus
355  self.m_wStatusBar['fg'] = fgColors['normal']
356  self.m_wStatusBar['state'] = 'readonly'
357  self.m_wStatusBar.grid(row=0, column=0, padx=3, pady=3, sticky=N+E+W+S)
358 
359 
360 
361 
362 # ------------------------------------------------------------------------------
363 # Class application
364 # ------------------------------------------------------------------------------
365 
366 ##
367 ## \brief Configure Hekateros servo.
368 ##
369 class application():
370 
371  ## \brief Unit test constructor.
372  def __init__(self):
373  self._Argv0 = __file__
374  self.m_win = None
375 
376  ## \brief Print usage error.
377  ##
378  ## \param emsg Error message string.
379  def printUsageErr(self, emsg):
380  if emsg:
381  print "%s: %s" % (self._Argv0, emsg)
382  else:
383  print "%s: error" % (self._Argv0)
384  print "Try '%s --help' for more information." % (self._Argv0)
385 
386  ## \brief Print Command-Line Usage Message.
387  def printUsage(self):
388  print \
389 """
390 usage: %s [OPTIONS] <arm> <joint>
391  %s --help
392 
393 Options and arguments:
394 -u, --uri=<device_uri> : Dynamixel serial device uri.
395  SYNTAX: [botsense://[hostname][:port]]/device
396  DEFAULT: /dev/ttyUSB0
397 -b, --baudrate=<baud> : Dynamixel serial device baud rate.
398  DEFAULT: 1000000
399 -h, --help : Display this help and exit.
400 
401 <arm> : Hekateros product id. One of: 4S 4L 5S 5L
402 <joint> : Hekateros joint servo. One of:
403  base_rot shoulder_m shoulder_s elbow
404  wrist_pitch wrist_rot gripper
405 """ % (self._Argv0, self._Argv0)
406 
407  ## \brief Get command-line options
408  ##
409  ## \param argv Argument list. If not None, then overrides
410  ## command-line arguments.
411  ## \param [out] kwargs Keyword argument list.
412  def getOptions(self, argv=None, **kwargs):
413  if argv is None:
414  argv = sys.argv
415 
416  self._Argv0 = kwargs.get('argv0', __file__)
417 
418  # defaults
419  kwargs['debug'] = 0
420  kwargs['uri'] = '/dev/ttyUSB0'
421  kwargs['baudrate'] = 1000000
422 
423  # parse command-line options
424  try:
425  opts, args = getopt.getopt(argv[1:], "?hu:b:",
426  ['help', 'uri=', 'baudrate='])
427  except getopt.error, msg:
428  raise usage(msg)
429  for opt, optarg in opts:
430  if opt in ('-h', '--help', '-?'):
431  self.printUsage()
432  sys.exit(0)
433  elif opt in ('-u', '--uri'):
434  kwargs['uri'] = optarg
435  elif opt in ('-b', '--baudrate'):
436  kwargs['baudrate'] = optarg
437 
438  if len(args) < 1:
439  self.printUsageErr("No arm product specified.")
440  sys.exit(2)
441  else:
442  kwargs['arm'] = args[0]
443 
444  if len(args) < 2:
445  self.printUsageErr("No servo id specified.")
446  sys.exit(2)
447  else:
448  kwargs['joint'] = args[1]
449 
450  return kwargs
451 
452  ## \brief Run application.
453  ##
454  ## \param argv Optional argument list to override command-line arguments.
455  ## \param kwargs Optional keyword argument list.
456  def run(self, argv=None, **kwargs):
457 
458  # parse command-line options and arguments
459  kwargs = self.getOptions(argv, **kwargs)
460 
461  if hekDynaScripts.has_key(kwargs['arm']):
462  scripts = hekDynaScripts[kwargs['arm']]
463  else:
464  self.printUsageErr("%s: Unknown hekateros product key." % (kwargs['arm']))
465  return 2
466 
467  if scripts.has_key(kwargs['joint']):
468  cfg_script = scripts[kwargs['joint']]
469  else:
470  self.printUsageErr("%s: Unknown joint." % (kwargs['joint']))
471  return 2
472 
473  # create root
474  root = Tk()
475 
476  # create application window
477  self.m_win = window(master=root, **kwargs)
478 
479  root.protocol('WM_DELETE_WINDOW', root.destroy)
480 
481  # go for it
482  self.m_win.mainloop()
483 
484  return 0
485 
486  fqscript = hekScriptPath + os.path.sep + cfg_script
487 
488  cmd = "dynashell --uri=%s --baudrate=%s --script=%s" % \
489  (kwargs['uri'], kwargs['baudrate'], fqscript)
490 
491  ec = os.system(cmd)
492 
493  return 0
494 
495 
496 # ------------------------------------------------------------------------------
497 # main
498 # ------------------------------------------------------------------------------
499 if __name__ == '__main__':
500  app = application();
501  sys.exit( app.run() );
Configure Hekateros servo.
Definition: dynacfg.py:369
def run(self, argv=None, kwargs)
Run application.
Definition: dynacfg.py:456
Window class supporting application.
Definition: dynacfg.py:193
def createHeading(self, parent, row, col)
Create top gui heading.
Definition: dynacfg.py:259
def __init__(self)
Unit test constructor.
Definition: dynacfg.py:372
def initData(self, kw)
Initialize class state data.
Definition: dynacfg.py:222
def createStatusBar(self, parent, row, col)
Create gui status bar at bottom of gui window.
Definition: dynacfg.py:343
def printUsage(self)
Print Command-Line Usage Message.
Definition: dynacfg.py:387
def getOptions(self, argv=None, kwargs)
Get command-line options.
Definition: dynacfg.py:412
def createWidgets(self)
Create gui widgets with supporting data and show.
Definition: dynacfg.py:251
def printUsageErr(self, emsg)
Print usage error.
Definition: dynacfg.py:379
def __init__(self, master=None, cnf={}, kw)
Constructor.
Definition: dynacfg.py:201
def createCfgPanel(self, parent, row, col)
Create joint state lower center panel headers.
Definition: dynacfg.py:302