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