/ lib / python / hal_glib.py
hal_glib.py
   1  #!/usr/bin/env python
   2  # vim: sts=4 sw=4 et
   3  
   4  import _hal, hal, gobject
   5  import linuxcnc
   6  import os
   7  import math
   8  
   9  # constants
  10  JOGJOINT  = 1
  11  JOGTELEOP = 0
  12  try:
  13      inifile = linuxcnc.ini(os.environ['INI_FILE_NAME'])
  14      trajcoordinates = inifile.find("TRAJ", "COORDINATES").lower().replace(" ","")
  15      jointcount = int(inifile.find("KINS","JOINTS"))
  16  except:
  17      pass
  18  
  19  class GPin(gobject.GObject, hal.Pin):
  20      __gtype_name__ = 'GPin'
  21      __gsignals__ = {'value-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())}
  22  
  23      REGISTRY = []
  24      UPDATE = False
  25  
  26      def __init__(self, *a, **kw):
  27          gobject.GObject.__init__(self)
  28          hal.Pin.__init__(self, *a, **kw)
  29          self._item_wrap(self._item)
  30          self._prev = None
  31          self.REGISTRY.append(self)
  32          self.update_start()
  33  
  34      def update(self):
  35          tmp = self.get()
  36          if tmp != self._prev:
  37              self.emit('value-changed')
  38          self._prev = tmp
  39  
  40      @classmethod
  41      def update_all(self):
  42          if not self.UPDATE:
  43              return
  44          kill = []
  45          for p in self.REGISTRY:
  46              try:
  47                  p.update()
  48              except:
  49                  kill.append(p)
  50                  print "Error updating pin %s; Removing" % p
  51          for p in kill:
  52              self.REGISTRY.remove(p)
  53          return self.UPDATE
  54  
  55      @classmethod
  56      def update_start(self, timeout=100):
  57          if GPin.UPDATE:
  58              return
  59          GPin.UPDATE = True
  60          gobject.timeout_add(timeout, self.update_all)
  61  
  62      @classmethod
  63      def update_stop(self, timeout=100):
  64          GPin.UPDATE = False
  65  
  66  class GComponent:
  67      def __init__(self, comp):
  68          if isinstance(comp, GComponent):
  69              comp = comp.comp
  70          self.comp = comp
  71  
  72      def newpin(self, *a, **kw): return GPin(_hal.component.newpin(self.comp, *a, **kw))
  73      def getpin(self, *a, **kw): return GPin(_hal.component.getpin(self.comp, *a, **kw))
  74  
  75      def exit(self, *a, **kw): return self.comp.exit(*a, **kw)
  76  
  77      def __getitem__(self, k): return self.comp[k]
  78      def __setitem__(self, k, v): self.comp[k] = v
  79  
  80  class _GStat(gobject.GObject):
  81      '''Emits signals based on linuxcnc status '''
  82      __gsignals__ = {
  83          'periodic': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  84          'state-estop': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  85          'state-estop-reset': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  86          'state-on': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  87          'state-off': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  88  
  89          'homed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
  90          'unhomed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
  91          'all-homed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  92          'not-all-homed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
  93          'override-limits-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN, gobject.TYPE_PYOBJECT,)),
  94  
  95          'hard-limits-tripped': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN, gobject.TYPE_PYOBJECT,)),
  96  
  97          'mode-manual': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  98          'mode-auto': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
  99          'mode-mdi': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 100  
 101          'interp-run': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 102          'interp-idle': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 103          'interp-paused': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 104          'interp-reading': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 105          'interp-waiting': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 106  
 107          'jograte-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 108          'jograte-angular-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 109          'jogincrement-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT, gobject.TYPE_STRING)),
 110          'jogincrement-angular-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT, gobject.TYPE_STRING)),
 111  
 112          'joint-selection-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 113          'axis-selection-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 114  
 115          'program-pause-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 116          'optional-stop-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 117          'block-delete-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 118  
 119          'file-loaded': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 120          'reload-display': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 121          'line-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 122  
 123          'tool-in-spindle-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 124          'tool-prep-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 125          'tool-info-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
 126          'current-tool-offset': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
 127  
 128          'motion-mode-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 129          'spindle-control-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,gobject.TYPE_INT)),
 130          'current-feed-rate': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 131          'current-x-rel-position': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 132          'current-position': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT,
 133                              gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,)),
 134          'current-z-rotation': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 135          'requested-spindle-speed-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 136          'actual-spindle-speed-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 137  
 138          'spindle-override-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 139          'feed-override-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 140          'rapid-override-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 141          'max-velocity-override-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_FLOAT,)),
 142  
 143          'feed-hold-enabled-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 144  
 145          'itime-mode': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 146          'fpm-mode': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 147          'fpr-mode': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 148          'css-mode': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 149          'rpm-mode': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 150          'radius-mode': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 151          'diameter-mode': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 152          'flood-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 153          'mist-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 154  
 155          'm-code-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 156          'g-code-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 157  
 158          'metric-mode-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN,)),
 159          'user-system-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 160  
 161          'mdi-line-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_STRING)),
 162          'gcode-line-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 163          'graphics-line-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 164          'graphics-gcode-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 165          'graphics-gcode-properties': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
 166          'graphics-view-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 167          'mdi-history-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 168          'machine-log-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 169          'update-machine-log': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_STRING)),
 170          'move-text-lineup': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 171          'move-text-linedown': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 172          'dialog-request': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
 173          'focus-overlay-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_BOOLEAN, gobject.TYPE_STRING,
 174                              gobject.TYPE_PYOBJECT)),
 175          'play-sound': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 176          'virtual-keyboard': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
 177          'dro-reference-change-request': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT,)),
 178          'show-preference': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 179          'shutdown': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 180          'error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_INT, gobject.TYPE_STRING)),
 181          'general': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
 182          'forced-update': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()),
 183          }
 184  
 185      STATES = { linuxcnc.STATE_ESTOP:       'state-estop'
 186               , linuxcnc.STATE_ESTOP_RESET: 'state-estop-reset'
 187               , linuxcnc.STATE_ON:          'state-on'
 188               , linuxcnc.STATE_OFF:         'state-off'
 189               }
 190  
 191      MODES  = { linuxcnc.MODE_MANUAL: 'mode-manual'
 192               , linuxcnc.MODE_AUTO:   'mode-auto'
 193               , linuxcnc.MODE_MDI:    'mode-mdi'
 194               }
 195  
 196      INTERP = { linuxcnc.INTERP_WAITING: 'interp-waiting'
 197               , linuxcnc.INTERP_READING: 'interp-reading'
 198               , linuxcnc.INTERP_PAUSED: 'interp-paused'
 199               , linuxcnc.INTERP_IDLE: 'interp-idle'
 200               }
 201  
 202      def __init__(self, stat = None):
 203          gobject.GObject.__init__(self)
 204          self.stat = stat or linuxcnc.stat()
 205          self.cmd = linuxcnc.command()
 206          self._status_active = False
 207          self.old = {}
 208          self.old['tool-prep-number'] = 0
 209          try:
 210              self.stat.poll()
 211              self.merge()
 212          except:
 213              pass
 214  
 215          self.current_jog_rate = 15
 216          self.current_angular_jog_rate = 360
 217          self.current_jog_distance = 0
 218          self.current_jog_distance_text =''
 219          self.current_jog_distance_angular= 0
 220          self.current_jog_distance_angular_text =''
 221          self.selected_joint = -1
 222          self.selected_axis = ''
 223          self._is_all_homed = False
 224          self.set_timer()
 225  
 226      # we put this in a function so qtvcp
 227      # can overide it to fix a seg fault
 228      def set_timer(self):
 229          gobject.timeout_add(100, self.update)
 230  
 231      def merge(self):
 232          self.old['state'] = self.stat.task_state
 233          self.old['mode']  = self.stat.task_mode
 234          self.old['interp']= self.stat.interp_state
 235          # Only update file if call level is 0, which
 236          # means we are not executing a subroutine/remap
 237          # This avoids emiting signals for bogus file names below
 238          if self.stat.call_level == 0:
 239              self.old['file']  = self.stat.file
 240          self.old['paused']= self.stat.paused
 241          self.old['line']  = self.stat.motion_line
 242          self.old['homed'] = self.stat.homed
 243          self.old['tool-in-spindle'] = self.stat.tool_in_spindle
 244          try:
 245              if hal.get_value('iocontrol.0.tool-prepare'):
 246                  self.old['tool-prep-number'] = hal.get_value('iocontrol.0.tool-prep-number')
 247          except RuntimeError:
 248               self.old['tool-prep-number'] = -1
 249          self.old['motion-mode'] = self.stat.motion_mode
 250          self.old['spindle-or'] = self.stat.spindle[0]['override']
 251          self.old['feed-or'] = self.stat.feedrate
 252          self.old['rapid-or'] = self.stat.rapidrate
 253          self.old['max-velocity-or'] = self.stat.max_velocity
 254          self.old['feed-hold']  = self.stat.feed_hold_enabled
 255          self.old['g5x-index']  = self.stat.g5x_index
 256          self.old['spindle-enabled']  = self.stat.spindle[0]['enabled']
 257          self.old['spindle-direction']  = self.stat.spindle[0]['direction']
 258          self.old['block-delete']= self.stat.block_delete
 259          self.old['optional-stop']= self.stat.optional_stop
 260          try:
 261              self.old['actual-spindle-speed'] = hal.get_value('spindle.0.speed-in') * 60
 262          except RuntimeError:
 263               self.old['actual-spindle-speed'] = 0
 264          self.old['flood']= self.stat.flood
 265          self.old['mist']= self.stat.mist
 266          self.old['current-z-rotation'] = self.stat.rotation_xy
 267          self.old['current-tool-offset'] = self.stat.tool_offset
 268  
 269          # override limits / hard limits
 270          or_limit_list=[]
 271          hard_limit_list = []
 272          hard_limit = False
 273          or_limit_set = False
 274          for j in range(0, self.stat.joints):
 275              or_limit_list.append( self.stat.joint[j]['override_limits'])
 276              or_limit_set = or_limit_set or self.stat.joint[j]['override_limits']
 277              min_hard_limit = self.stat.joint[j]['min_hard_limit']
 278              max_hard_limit = self.stat.joint[j]['max_hard_limit']
 279              hard_limit = hard_limit or min_hard_limit or max_hard_limit
 280              hard_limit_list.append([min_hard_limit,max_hard_limit])
 281          self.old['override-limits'] = or_limit_list
 282          self.old['override-limits-set'] = bool(or_limit_set)
 283          self.old['hard-limits-tripped'] = bool(hard_limit)
 284          self.old['hard-limits-list'] = hard_limit_list
 285  
 286          # active G codes
 287          active_gcodes = []
 288          codes =''
 289          for i in sorted(self.stat.gcodes[1:]):
 290              if i == -1: continue
 291              if i % 10 == 0:
 292                      active_gcodes.append("G%d" % (i/10))
 293              else:
 294                      active_gcodes.append("G%d.%d" % (i/10, i%10))
 295          for i in active_gcodes:
 296              codes = codes +('%s '%i)
 297          self.old['g-code'] = codes
 298          # extract specific G code modes
 299          itime = fpm = fpr = css = rpm = metric = False
 300          radius = diameter = False
 301          for num,i in enumerate(active_gcodes):
 302              if i == 'G93': itime = True
 303              elif i == 'G94': fpm = True
 304              elif i == 'G95': fpr = True
 305              elif i == 'G96': css = True
 306              elif i == 'G97': rpm = True
 307              elif i == 'G21': metric = True
 308              elif i == 'G7': diameter  = True
 309              elif i == 'G8': radius = True
 310          self.old['itime'] = itime
 311          self.old['fpm'] = fpm
 312          self.old['fpr'] = fpr
 313          self.old['css'] = css
 314          self.old['rpm'] = rpm
 315          self.old['metric'] = metric
 316          self.old['radius'] = radius
 317          self.old['diameter'] = diameter
 318          if css:
 319              self.old['spindle-speed']= hal.get_value('spindle.0.speed-out')
 320          else:
 321              self.old['spindle-speed']= self.stat.spindle[0]['speed']
 322  
 323          # active M codes
 324          active_mcodes = []
 325          mcodes = ''
 326          for i in sorted(self.stat.mcodes[1:]):
 327              if i == -1: continue
 328              active_mcodes.append("M%d"%i )
 329          for i in active_mcodes:
 330              mcodes = mcodes + ("%s "%i)
 331              #active_mcodes.append("M%s "%i)
 332          self.old['m-code'] = mcodes
 333          self.old['tool-info']  = self.stat.tool_table[0]
 334  
 335      def update(self):
 336          try:
 337              self.stat.poll()
 338          except:
 339              self._status_active = False
 340              # some things might not need linuxcnc status but do need periodic
 341              self.emit('periodic')
 342              # Reschedule
 343              return True
 344          self._status_active = True
 345          old = dict(self.old)
 346          self.merge()
 347  
 348          state_old = old.get('state', 0)
 349          state_new = self.old['state']
 350          if state_new != state_old:
 351              if state_new > linuxcnc.STATE_ESTOP:
 352                  self.emit('state-estop-reset')
 353              else:
 354                  self.emit('state-estop')
 355              self.emit('state-off')
 356              self.emit('interp-idle')
 357  
 358          if state_new != state_old:
 359              if state_old == linuxcnc.STATE_ON and state_new < linuxcnc.STATE_ON:
 360                  self.emit('state-off')
 361              self.emit(self.STATES[state_new])
 362              if state_new == linuxcnc.STATE_ON:
 363                  old['mode'] = 0
 364                  old['interp'] = 0
 365  
 366          mode_old = old.get('mode', 0)
 367          mode_new = self.old['mode']
 368          if mode_new != mode_old:
 369              self.emit(self.MODES[mode_new])
 370  
 371          interp_old = old.get('interp', 0)
 372          interp_new = self.old['interp']
 373          if interp_new != interp_old:
 374              if not interp_old or interp_old == linuxcnc.INTERP_IDLE:
 375                  self.emit('interp-run')
 376              self.emit(self.INTERP[interp_new])
 377          # paused
 378          paused_old = old.get('paused', None)
 379          paused_new = self.old['paused']
 380          if paused_new != paused_old:
 381              self.emit('program-pause-changed',paused_new)
 382          # block delete
 383          block_delete_old = old.get('block-delete', None)
 384          block_delete_new = self.old['block-delete']
 385          if block_delete_new != block_delete_old:
 386              self.emit('block-delete-changed',block_delete_new)
 387          # optional_stop
 388          optional_stop_old = old.get('optional-stop', None)
 389          optional_stop_new = self.old['optional-stop']
 390          if optional_stop_new != optional_stop_old:
 391              self.emit('optional-stop-changed',optional_stop_new)
 392          # file changed
 393          file_old = old.get('file', None)
 394          file_new = self.old['file']
 395          if file_new != file_old:
 396              # if interpreter is reading or waiting, the new file
 397              # is a remap procedure, with the following test we
 398              # partly avoid emitting a signal in that case, which would cause
 399              # a reload of the preview and sourceview widgets.  A signal could
 400              # still be emitted if aborting a program shortly after it ran an
 401              # external file subroutine, but that is fixed by not updating the
 402              # file name if call level != 0 in the merge() function above.
 403              # do avoid that a signal is emited in that case, causing
 404              # a reload of the preview and sourceview widgets
 405              if self.stat.interp_state == linuxcnc.INTERP_IDLE:
 406                  self.emit('file-loaded', file_new)
 407  
 408          #ToDo : Find a way to avoid signal when the line changed due to
 409          #       a remap procedure, because the signal do highlight a wrong
 410          #       line in the code
 411          # current line
 412          line_old = old.get('line', None)
 413          line_new = self.old['line']
 414          if line_new != line_old:
 415              self.emit('line-changed', line_new)
 416  
 417          tool_old = old.get('tool-in-spindle', None)
 418          tool_new = self.old['tool-in-spindle']
 419          if tool_new != tool_old:
 420              self.emit('tool-in-spindle-changed', tool_new)
 421          tool_num_old = old.get('tool-prep-number')
 422          tool_num_new = self.old['tool-prep-number']
 423          if tool_num_new != tool_num_old:
 424              self.emit('tool-prep-changed', tool_num_new)
 425  
 426          motion_mode_old = old.get('motion-mode', None)
 427          motion_mode_new = self.old['motion-mode']
 428          if motion_mode_new != motion_mode_old:
 429              self.emit('motion-mode-changed', motion_mode_new)
 430  
 431          # if the homed status has changed
 432          # check number of homed joints against number of available joints
 433          # if they are equal send the all-homed signal
 434          # else send the not-all-homed signal (with a string of unhomed joint numbers)
 435          # if a joint is homed send 'homed' (with a string of homed joint number)
 436          homed_joint_old = old.get('homed', None)
 437          homed_joint_new = self.old['homed']
 438          if homed_joint_new != homed_joint_old:
 439              homed_joints = 0
 440              unhomed_joints = ""
 441              for joint in range(0, self.stat.joints):
 442                  if self.stat.homed[joint]:
 443                      homed_joints += 1
 444                      self.emit('homed', joint)
 445                  else:
 446                      self.emit('unhomed', joint)
 447                      unhomed_joints += str(joint)
 448              if homed_joints == self.stat.joints:
 449                  self._is_all_homed = True
 450                  self.emit('all-homed')
 451              else:
 452                  self._is_all_homed = False
 453                  self.emit('not-all-homed', unhomed_joints)
 454  
 455          # override limts
 456          or_limits_old = old.get('override-limits', None)
 457          or_limits_new = self.old['override-limits']
 458          or_limits_set_new = self.old['override-limits-set']
 459          if or_limits_new != or_limits_old:
 460              self.emit('override-limits-changed',or_limits_set_new, or_limits_new)
 461          # hard limits tripped
 462          t_list_old = old.get('hard-limits-list')
 463          t_list_new = self.old['hard-limits-list']
 464          if t_list_new != t_list_old:
 465              hard_limits_tripped_new = self.old['hard-limits-tripped']
 466              self.emit('hard-limits-tripped',hard_limits_tripped_new, t_list_new)
 467          # current velocity
 468          self.emit('current-feed-rate',self.stat.current_vel * 60.0)
 469          # X relative position
 470          position = self.stat.actual_position[0]
 471          g5x_offset = self.stat.g5x_offset[0]
 472          tool_offset = self.stat.tool_offset[0]
 473          g92_offset = self.stat.g92_offset[0]
 474          self.emit('current-x-rel-position',position-g5x_offset-tool_offset-g92_offset)
 475  
 476          # calculate position offsets (native units)
 477          p,rel_p,dtg = self.get_position()
 478          self.emit('current_position',p, rel_p, dtg, self.stat.joint_actual_position)
 479  
 480          # spindle control
 481          spindle_enabled_old = old.get('spindle-enabled', None)
 482          spindle_enabled_new = self.old['spindle-enabled']
 483          spindle_direction_old = old.get('spindle-direction', None)
 484          spindle_direction_new = self.old['spindle-direction']
 485          if spindle_enabled_new != spindle_enabled_old or spindle_direction_new != spindle_direction_old:
 486              self.emit('spindle-control-changed', spindle_enabled_new, spindle_direction_new)
 487          # requested spindle speed
 488          spindle_spd_old = old.get('spindle-speed', None)
 489          spindle_spd_new = self.old['spindle-speed']
 490          if spindle_spd_new != spindle_spd_old:
 491              self.emit('requested-spindle-speed-changed', spindle_spd_new)
 492          # actual spindle speed
 493          act_spindle_spd_old = old.get('actual-spindle-speed', None)
 494          act_spindle_spd_new = self.old['actual-spindle-speed']
 495          if act_spindle_spd_new != act_spindle_spd_old:
 496              self.emit('actual-spindle-speed-changed', act_spindle_spd_new)
 497          # spindle override
 498          spindle_or_old = old.get('spindle-or', None)
 499          spindle_or_new = self.old['spindle-or']
 500          if spindle_or_new != spindle_or_old:
 501              self.emit('spindle-override-changed',spindle_or_new * 100)
 502          # feed override
 503          feed_or_old = old.get('feed-or', None)
 504          feed_or_new = self.old['feed-or']
 505          if feed_or_new != feed_or_old:
 506              self.emit('feed-override-changed',feed_or_new * 100)
 507          # rapid override
 508          rapid_or_old = old.get('rapid-or', None)
 509          rapid_or_new = self.old['rapid-or']
 510          if rapid_or_new != rapid_or_old:
 511              self.emit('rapid-override-changed',rapid_or_new * 100)
 512          # max-velocity override
 513          max_velocity_or_old = old.get('max-velocity-or', None)
 514          max_velocity_or_new = self.old['max-velocity-or']
 515          if max_velocity_or_new != max_velocity_or_old:
 516              self.emit('max-velocity-override-changed',max_velocity_or_new * 60)
 517          # feed hold
 518          feed_hold_old = old.get('feed-hold', None)
 519          feed_hold_new = self.old['feed-hold']
 520          if feed_hold_new != feed_hold_old:
 521              self.emit('feed-hold-enabled-changed',feed_hold_new)
 522          # mist
 523          mist_old = old.get('mist', None)
 524          mist_new = self.old['mist']
 525          if mist_new != mist_old:
 526              self.emit('mist-changed',mist_new)
 527          # flood
 528          flood_old = old.get('flood', None)
 529          flood_new = self.old['flood']
 530          if flood_new != flood_old:
 531              self.emit('flood-changed',flood_new)
 532          # rotation around Z
 533          z_rot_old = old.get('current-z-rotation', None)
 534          z_rot_new = self.old['current-z-rotation']
 535          if z_rot_new != z_rot_old:
 536              self.emit('current-z-rotation',z_rot_new)
 537          # current tool offsets
 538          tool_off_old = old.get('current-tool-offset', None)
 539          tool_off_new = self.old['current-tool-offset']
 540          if tool_off_new != tool_off_old:
 541                 self.emit('current-tool-offset',tool_off_new)
 542          #############################
 543          # Gcodes
 544          #############################
 545          # G codes
 546          g_code_old = old.get('g-code', None)
 547          g_code_new = self.old['g-code']
 548          if g_code_new != g_code_old:
 549              self.emit('g-code-changed',g_code_new)
 550          # metric mode g21
 551          metric_old = old.get('metric', None)
 552          metric_new = self.old['metric']
 553          if metric_new != metric_old:
 554              self.emit('metric-mode-changed',metric_new)
 555          # G5x (active user system)
 556          g5x_index_old = old.get('g5x-index', None)
 557          g5x_index_new = self.old['g5x-index']
 558          if g5x_index_new != g5x_index_old:
 559              self.emit('user-system-changed',g5x_index_new)
 560          # inverse time mode g93
 561          itime_old = old.get('itime', None)
 562          itime_new = self.old['itime']
 563          if itime_new != itime_old:
 564              self.emit('itime-mode',itime_new)
 565          # feed per minute mode g94
 566          fpm_old = old.get('fpm', None)
 567          fpm_new = self.old['fpm']
 568          if fpm_new != fpm_old:
 569              self.emit('fpm-mode',fpm_new)
 570          # feed per revolution mode g95
 571          fpr_old = old.get('fpr', None)
 572          fpr_new = self.old['fpr']
 573          if fpr_new != fpr_old:
 574              self.emit('fpr-mode',fpr_new)
 575          # css mode g96
 576          css_old = old.get('css', None)
 577          css_new = self.old['css']
 578          if css_new != css_old:
 579              self.emit('css-mode',css_new)
 580          # rpm mode g97
 581          rpm_old = old.get('rpm', None)
 582          rpm_new = self.old['rpm']
 583          if rpm_new != rpm_old:
 584              self.emit('rpm-mode',rpm_new)
 585          # radius mode g8
 586          radius_old = old.get('radius', None)
 587          radius_new = self.old['radius']
 588          if radius_new != radius_old:
 589              self.emit('radius-mode',radius_new)
 590          # diameter mode g7
 591          diam_old = old.get('diameter', None)
 592          diam_new = self.old['diameter']
 593          if diam_new != diam_old:
 594              self.emit('diameter-mode',diam_new)
 595          ####################################
 596          # Mcodes
 597          ####################################
 598          # M codes
 599          m_code_old = old.get('m-code', None)
 600          m_code_new = self.old['m-code']
 601          if m_code_new != m_code_old:
 602              self.emit('m-code-changed',m_code_new)
 603          tool_info_old = old.get('tool-info', None)
 604          tool_info_new = self.old['tool-info']
 605          if tool_info_new != tool_info_old:
 606              self.emit('tool-info-changed', tool_info_new)
 607  
 608          # AND DONE... Return true to continue timeout
 609          self.emit('periodic')
 610          return True
 611  
 612      def forced_update(self):
 613          try:
 614              self.stat.poll()
 615          except:
 616              # Reschedule
 617              return True
 618          self.merge()
 619          state_new = self.old['state']
 620          if state_new > linuxcnc.STATE_ESTOP:
 621              self.emit('state-estop-reset')
 622          else:
 623              self.emit('state-estop')
 624          self.emit('state-off')
 625          self.emit('interp-idle')
 626          # override limits
 627          or_limits_new = self.old['override-limits']
 628          or_limits_set_new = self.old['override-limits-set']
 629          self.emit('override-limits-changed',or_limits_set_new, or_limits_new)
 630          # overrides
 631          feed_or_new = self.old['feed-or']
 632          self.emit('feed-override-changed',feed_or_new * 100)
 633          rapid_or_new = self.old['rapid-or']
 634          self.emit('rapid-override-changed',rapid_or_new  * 100)
 635          max_velocity_or_new = self.old['max-velocity-or']
 636          self.emit('max-velocity-override-changed',max_velocity_or_new * 60)
 637          spindle_or_new = self.old['spindle-or']
 638          self.emit('spindle-override-changed',spindle_or_new  * 100)
 639  
 640          # spindle speed mpde
 641          css_new = self.old['css']
 642          self.emit('css-mode',css_new)
 643          rpm_new = self.old['rpm']
 644          self.emit('rpm-mode',rpm_new)
 645  
 646          # feed mode:
 647          itime_new = self.old['itime']
 648          self.emit('itime-mode',itime_new)
 649          fpm_new = self.old['fpm']
 650          self.emit('fpm-mode',fpm_new)
 651          fpr_new = self.old['fpr']
 652          self.emit('fpr-mode',fpr_new)
 653          # paused
 654          paused_new = self.old['paused']
 655          self.emit('program-pause-changed',paused_new)
 656          # block delete
 657          block_delete_new = self.old['block-delete']
 658          self.emit('block-delete-changed',block_delete_new)
 659          # optional_stop
 660          optional_stop_new = self.old['optional-stop']
 661          self.emit('optional-stop-changed',optional_stop_new)
 662          # user system G5x
 663          system_new = self.old['g5x-index']
 664          self.emit('user-system-changed',system_new)
 665          # radius mode g8
 666          radius_new = self.old['radius']
 667          self.emit('radius-mode',radius_new)
 668          # diameter mode g7
 669          diam_new = self.old['diameter']
 670          self.emit('diameter-mode',diam_new)
 671          # rotation around Z
 672          z_rot_new = self.old['current-z-rotation']
 673          self.emit('current-z-rotation',z_rot_new)
 674          # current tool offsets
 675          tool_off_new = self.old['current-tool-offset']
 676          self.emit('current-tool-offset',tool_off_new)
 677  
 678          # M codes
 679          m_code_new = self.old['m-code']
 680          self.emit('m-code-changed',m_code_new)
 681          flood_new = self.old['flood']
 682          self.emit('flood-changed',flood_new)
 683          mist_new = self.old['mist']
 684          self.emit('mist-changed',mist_new)
 685  
 686          # G codes
 687          g_code_new = self.old['g-code']
 688          self.emit('g-code-changed',g_code_new)
 689          # metric units G21
 690          metric_new = self.old['metric']
 691          self.emit('metric_mode_changed',metric_new)
 692          # tool in spindle
 693          tool_new = self.old['tool-in-spindle']
 694          self.emit('tool-in-spindle-changed', tool_new)
 695          tool_num_new = self.old['tool-prep-number']
 696          self.emit('tool-prep-changed', tool_num_new)
 697  
 698          # Trajectory Motion mode
 699          motion_mode_new = self.old['motion-mode']
 700          self.emit('motion-mode-changed', motion_mode_new)
 701  
 702          # Spindle requested speed
 703          spindle_spd_new = self.old['spindle-speed']
 704          self.emit('requested-spindle-speed-changed', spindle_spd_new)
 705          spindle_spd_new = self.old['actual-spindle-speed']
 706          self.emit('actual-spindle-speed-changed', spindle_spd_new)
 707          self.emit('jograte-changed', self.current_jog_rate)
 708          self.emit('jograte-angular-changed', self.current_angular_jog_rate)
 709          self.emit('jogincrement-changed', self.current_jog_distance, self.current_jog_distance_text)
 710          self.emit('jogincrement-angular-changed', self.current_jog_distance_angular, self.current_jog_distance_angular_text)
 711          tool_info_new = self.old['tool-info']
 712          self.emit('tool-info-changed', tool_info_new)
 713  
 714          # update external ojects
 715          self.emit('forced-update')
 716  
 717      # ********** Helper function ********************
 718      def get_position(self):
 719          p = self.stat.actual_position
 720          mp = self.stat.position
 721          dtg = self.stat.dtg
 722  
 723          x = p[0] - self.stat.g5x_offset[0] - self.stat.tool_offset[0]
 724          y = p[1] - self.stat.g5x_offset[1] - self.stat.tool_offset[1]
 725          z = p[2] - self.stat.g5x_offset[2] - self.stat.tool_offset[2]
 726          a = p[3] - self.stat.g5x_offset[3] - self.stat.tool_offset[3]
 727          b = p[4] - self.stat.g5x_offset[4] - self.stat.tool_offset[4]
 728          c = p[5] - self.stat.g5x_offset[5] - self.stat.tool_offset[5]
 729          u = p[6] - self.stat.g5x_offset[6] - self.stat.tool_offset[6]
 730          v = p[7] - self.stat.g5x_offset[7] - self.stat.tool_offset[7]
 731          w = p[8] - self.stat.g5x_offset[8] - self.stat.tool_offset[8]
 732  
 733          if self.stat.rotation_xy != 0:
 734              t = math.radians(-self.stat.rotation_xy)
 735              xr = x * math.cos(t) - y * math.sin(t)
 736              yr = x * math.sin(t) + y * math.cos(t)
 737              x = xr
 738              y = yr
 739  
 740          x -= self.stat.g92_offset[0]
 741          y -= self.stat.g92_offset[1]
 742          z -= self.stat.g92_offset[2]
 743          a -= self.stat.g92_offset[3]
 744          b -= self.stat.g92_offset[4]
 745          c -= self.stat.g92_offset[5]
 746          u -= self.stat.g92_offset[6]
 747          v -= self.stat.g92_offset[7]
 748          w -= self.stat.g92_offset[8]
 749  
 750          relp = [x, y, z, a, b, c, u, v, w]
 751          return p,relp,dtg
 752  
 753      # check for requied modes
 754      # fail if mode is 0
 755      # fail if machine is busy
 756      # true if all ready in mode
 757      # None if possible to change
 758      def check_for_modes(self, *modes):
 759          def running(s):
 760              return s.task_mode == linuxcnc.MODE_AUTO and s.interp_state != linuxcnc.INTERP_IDLE
 761          self.stat.poll()
 762          premode = self.stat.task_mode
 763          if not modes: return (None, premode)
 764          if  self.stat.task_mode in modes: return (True, premode)
 765          if running( self.stat): return (None, premode)
 766          return (False, premode)
 767  
 768      def get_current_mode(self):
 769          return self.old['mode']
 770  
 771      # linear - in machine units
 772      def set_jograte(self, upm):
 773          self.current_jog_rate = upm
 774          self.emit('jograte-changed', upm)
 775  
 776      def get_jograte(self):
 777          return self.current_jog_rate
 778  
 779      def set_jograte_angular(self,rate):
 780          self.current_angular_jog_rate = rate
 781          self.emit('jograte-angular-changed', rate)
 782  
 783      def get_jograte_angular(self):
 784          return self.current_angular_jog_rate
 785  
 786      def get_jog_increment_angular(self):
 787          return self.current_jog_distance_angular
 788  
 789      def set_jog_increment_angular(self, distance, text):
 790          self.current_jog_distance_angular = distance
 791          self.current_jog_distance_text_angular = text
 792          self.emit('jogincrement-angular-changed', distance, text)
 793  
 794      # should be in machine units
 795      def set_jog_increments(self, distance, text):
 796          self.current_jog_distance = distance
 797          self.current_jog_distance_text = text
 798          self.emit('jogincrement-changed', distance, text)
 799  
 800      def get_jog_increment(self):
 801          return self.current_jog_distance
 802  
 803      def set_selected_joint(self, data):
 804          self.selected_joint = int(data)
 805          self.emit('joint-selection-changed', data)
 806  
 807      def get_selected_joint(self):
 808          return self.selected_joint
 809  
 810      def set_selected_axis(self, data):
 811          self.selected_axis = str(data)
 812          self.emit('axis-selection-changed', data)
 813  
 814      def get_selected_axis(self):
 815          return self.selected_axis
 816      def is_all_homed(self):
 817          return self._is_all_homed
 818  
 819      def machine_is_on(self):
 820          return self.old['state']  > linuxcnc.STATE_OFF
 821  
 822      def estop_is_clear(self):
 823          return self.old['state'] > linuxcnc.STATE_ESTOP
 824  
 825      def is_man_mode(self):
 826          self.stat.poll()
 827          return self.stat.task_mode  == linuxcnc.MODE_MANUAL
 828  
 829      def is_mdi_mode(self):
 830          self.stat.poll()
 831          return self.stat.task_mode  == linuxcnc.MODE_MDI
 832  
 833      def is_auto_mode(self):
 834          self.stat.poll()
 835          return self.stat.task_mode  == linuxcnc.MODE_AUTO
 836  
 837      def is_on_and_idle(self):
 838          self.stat.poll()
 839          return self.stat.task_state > linuxcnc.STATE_OFF and self.stat.interp_state == linuxcnc.INTERP_IDLE
 840  
 841      def is_auto_running(self):
 842          self.stat.poll()
 843          return self.stat.task_mode == linuxcnc.MODE_AUTO and self.stat.interp_state != linuxcnc.INTERP_IDLE
 844  
 845      def is_auto_paused(self):
 846          return self.old['paused']
 847  
 848      def is_interp_running(self):
 849          self.stat.poll()
 850          return self.stat.interp_state != linuxcnc.INTERP_IDLE
 851  
 852      def is_interp_paused(self):
 853          self.stat.poll()
 854          return self.stat.interp_state == linuxcnc.INTERP_PAUSED
 855  
 856      def is_interp_reading(self):
 857          self.stat.poll()
 858          return self.stat.interp_state == linuxcnc.INTERP_READING
 859  
 860      def is_interp_waiting(self):
 861          self.stat.poll()
 862          return self.stat.interp_state == linuxcnc.INTERP_WAITING
 863  
 864      def is_interp_idle(self):
 865          self.stat.poll()
 866          return self.stat.interp_state == linuxcnc.INTERP_IDLE
 867  
 868      def is_file_loaded(self):
 869          self.stat.poll()
 870          if self.stat.file:
 871              return True
 872          else:
 873              return False
 874  
 875      def is_metric_mode(self):
 876          return self.old['metric']
 877  
 878      def is_spindle_on(self):
 879          return self.old['spindle-enabled']
 880  
 881      def is_joint_mode(self):
 882          try:
 883              self.stat.poll()
 884          except:
 885              return None
 886          return bool(self.stat.motion_mode == linuxcnc.TRAJ_MODE_FREE)
 887  
 888      def is_status_valid(self):
 889          return self._status_active
 890  
 891      def is_limits_override_set(self):
 892          return self.old['override-limits-set']
 893  
 894      def is_hard_limits_tripped(self):
 895          return self.old['hard-limits-tripped']
 896  
 897      def get_current_tool(self):
 898          self.stat.poll()
 899          return self.stat.tool_in_spindle
 900  
 901      def set_tool_touchoff(self,tool,axis,value):
 902          premode = None
 903          m = "G10 L10 P%d %s%f"%(tool,axis,value)
 904          self.stat.poll()
 905          if self.stat.task_mode != linuxcnc.MODE_MDI:
 906              premode = self.stat.task_mode
 907              self.cmd.mode(linuxcnc.MODE_MDI)
 908              self.cmd.wait_complete()
 909          self.cmd.mdi(m)
 910          self.cmd.wait_complete()
 911          self.cmd.mdi("g43")
 912          self.cmd.wait_complete()
 913          if premode:
 914              self.cmd.mode(premode)
 915  
 916      def set_axis_origin(self,axis,value):
 917          premode = None
 918          m = "G10 L20 P0 %s%f"%(axis,value)
 919          self.stat.poll()
 920          if self.stat.task_mode != linuxcnc.MODE_MDI:
 921              premode = self.stat.task_mode
 922              self.cmd.mode(linuxcnc.MODE_MDI)
 923              self.cmd.wait_complete()
 924          self.cmd.mdi(m)
 925          self.cmd.wait_complete()
 926          if premode:
 927              self.cmd.mode(premode)
 928          self.emit('reload-display')
 929  
 930      def do_jog(self, axisnum, direction, distance=0):
 931          jjogmode,j_or_a = self.get_jog_info(axisnum)
 932          if direction == 0:
 933              self.cmd.jog(linuxcnc.JOG_STOP, jjogmode, j_or_a)
 934          else:
 935              if axisnum in (3,4,5):
 936                  rate = self.current_angular_jog_rate
 937              else:
 938                  rate = self.current_jog_rate/60
 939              if distance == 0:
 940                  self.cmd.jog(linuxcnc.JOG_CONTINUOUS, jjogmode, j_or_a, direction * rate)
 941              else:
 942                  self.cmd.jog(linuxcnc.JOG_INCREMENT, jjogmode, j_or_a, direction * rate, distance)
 943  
 944      def get_jjogmode(self):
 945          self.stat.poll()
 946          if self.stat.motion_mode == linuxcnc.TRAJ_MODE_FREE:
 947              return JOGJOINT
 948          if self.stat.motion_mode == linuxcnc.TRAJ_MODE_TELEOP:
 949              return JOGTELEOP
 950          print "commands.py: unexpected motion_mode",self.stat.motion_mode
 951          return JOGTELEOP
 952  
 953      def jnum_for_axisnum(self,axisnum):
 954          if self.stat.kinematics_type != linuxcnc.KINEMATICS_IDENTITY:
 955              print ("\n%s:\n  Joint jogging not supported for"
 956                     "non-identity kinematics"%__file__)
 957              return -1 # emcJogCont() et al reject neg joint/axis no.s
 958          jnum = trajcoordinates.index( "xyzabcuvw"[axisnum] )
 959          if jnum > jointcount:
 960              print ("\n%s:\n  Computed joint number=%d for axisnum=%d "
 961                     "exceeds jointcount=%d with trajcoordinates=%s"
 962                     %(__file__,jnum,axisnum,jointcount,trajcoordinates))
 963              # Note: primary gui should protect for this misconfiguration
 964              # decline to jog
 965              return -1 # emcJogCont() et al reject neg joint/axis no.s
 966          return jnum
 967  
 968      def get_jog_info (self,axisnum):
 969          jjogmode = self.get_jjogmode()
 970          j_or_a = axisnum
 971          if jjogmode == JOGJOINT: j_or_a = self.jnum_for_axisnum(axisnum)
 972          return jjogmode,j_or_a
 973  
 974      def get_probed_position_with_offsets(self) :
 975          self.stat.poll()
 976          probed_position=list(self.stat.probed_position)
 977          coord=list(self.stat.probed_position)
 978          g5x_offset=list(self.stat.g5x_offset)
 979          g92_offset=list(self.stat.g92_offset)
 980          tool_offset=list(self.stat.tool_offset)
 981          for i in range(0, len(probed_position)-1):
 982               coord[i] = probed_position[i] - g5x_offset[i] - g92_offset[i] - tool_offset[i]
 983          angl=self.stat.rotation_xy
 984          res=self._rott00_point(coord[0],coord[1],-angl)
 985          coord[0]=res[0]
 986          coord[1]=res[1]
 987          return coord
 988  
 989      # rotate around 0,0 point coordinates
 990      def _rott00_point(self,x1=0.,y1=0.,a1=0.) :
 991          coord = [x1,y1]
 992          if a1 != 0:
 993              t = math.radians(a1)
 994              coord[0] = x1 * math.cos(t) - y1 * math.sin(t)
 995              coord[1] = x1 * math.sin(t) + y1 * math.cos(t)
 996          return coord
 997  
 998      def shutdown(self):
 999          self.emit('shutdown')
1000  
1001      def __getitem__(self, item):
1002          return getattr(self, item)
1003      def __setitem__(self, item, value):
1004          return setattr(self, item, value)
1005  
1006  class GStat(_GStat):
1007      _instance = None
1008      def __new__(cls, *args, **kwargs):
1009          if not cls._instance:
1010              cls._instance = _GStat.__new__(cls, *args, **kwargs)
1011          return cls._instance