Source code for qtpyvcp.actions.machine_actions

import linuxcnc
from qtpy.QtWidgets import QComboBox

from qtpyvcp.utilities.settings import setting

# Set up logging
from qtpyvcp.utilities import logger
LOG = logger.getLogger(__name__)

from qtpyvcp.actions.base_actions import setTaskMode
from qtpyvcp.plugins import getPlugin

STATUS = getPlugin('status')
STAT = STATUS.stat

from qtpyvcp.utilities.info import Info
INFO = Info()
CMD = linuxcnc.command()


# -------------------------------------------------------------------------
# E-STOP action
# -------------------------------------------------------------------------
[docs]class estop: """E-Stop action group"""
[docs] @staticmethod def activate(): """Set E-Stop active ActionButton syntax:: machine.estop.activate """ LOG.debug("Setting state red<ESTOP>") CMD.state(linuxcnc.STATE_ESTOP)
[docs] @staticmethod def reset(): """Resets E-Stop ActionButton syntax:: machine.estop.reset """ LOG.debug("Setting state green<ESTOP_RESET>") CMD.state(linuxcnc.STATE_ESTOP_RESET)
[docs] @staticmethod def toggle(): """Toggles E-Stop state ActionButton syntax:: machine.estop.toggle """ if estop.is_activated(): estop.reset() else: estop.activate()
[docs] @staticmethod def is_activated(): """Checks if E_Stop is activated. Returns: bool : True if E-Stop is active, else False. """ return bool(STAT.estop)
def _estop_ok(widget=None): # E-Stop is ALWAYS ok, but provide this method for consistency _estop_ok.msg = "" return True def _estop_bindOk(widget): widget.setChecked(STAT.estop != linuxcnc.STATE_ESTOP) STATUS.estop.onValueChanged(lambda v: widget.setChecked(not v)) estop.activate.ok = estop.reset.ok = estop.toggle.ok = _estop_ok estop.activate.bindOk = estop.reset.bindOk = estop.toggle.bindOk = _estop_bindOk # ------------------------------------------------------------------------- # POWER action # -------------------------------------------------------------------------
[docs]class power: """Power action group"""
[docs] @staticmethod def on(): """Turns machine power On ActionButton syntax:: machine.power.on """ LOG.debug("Setting state green<ON>") CMD.state(linuxcnc.STATE_ON)
[docs] @staticmethod def off(): """Turns machine power Off ActionButton syntax:: machine.power.off """ LOG.debug("Setting state red<OFF>") CMD.state(linuxcnc.STATE_OFF)
[docs] @staticmethod def toggle(): """Toggles machine power On/Off ActionButton syntax:: machine.power.toggle """ if power.is_on(): power.off() else: power.on()
[docs] @staticmethod def is_on(): """Checks if power is on. Returns: bool : True if power is on, else False. """ return STAT.task_state == linuxcnc.STATE_ON
def _power_ok(widget=None): if STAT.task_state == linuxcnc.STATE_ESTOP_RESET: ok = True msg = "" else: ok = False msg = "Can't turn machine ON until out of E-Stop" _power_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _power_bindOk(widget): _power_ok(widget) widget.setChecked(STAT.task_state == linuxcnc.STATE_ON) STATUS.estop.onValueChanged(lambda: _power_ok(widget)) STATUS.on.notify(lambda v: widget.setChecked(v)) power.on.ok = power.off.ok = power.toggle.ok = _power_ok power.on.bindOk = power.off.bindOk = power.toggle.bindOk = _power_bindOk # ------------------------------------------------------------------------- # MDI action # ------------------------------------------------------------------------- PREVIOUS_MODE = None def _resetMode(interp_state): global PREVIOUS_MODE if PREVIOUS_MODE is not None and interp_state == linuxcnc.INTERP_IDLE: if setTaskMode(PREVIOUS_MODE): LOG.debug("Successfully reset task_mode after MDI") PREVIOUS_MODE = None STATUS.interp_state.onValueChanged(_resetMode)
[docs]def issue_mdi(command, reset=True): """Issue an MDI command. An MDI command can be issued any time the machine is homed (if not NO_FORCE_HOMING in the INI) and the interpreter is IDLE. The task mode will automatically be switched to MDI prior to issuing the command and will be returned to the previous mode when the interpreter becomes IDLE. ActionButton syntax to issue G0 X5: :: machine.issue_mdi:G0X5 It is simpler to use the MDIButton, examples in the Widgets section. Args: command (str) : A valid RS274 gcode command string. Multiple MDI commands can be separated with a ``;`` and will be issued sequentially. reset (bool, optional): Whether to reset the Task Mode to the state the machine was in prior to issuing the MDI command. """ if reset: # save the previous mode global PREVIOUS_MODE PREVIOUS_MODE = STAT.task_mode # Force `interp_state` update on next status cycle. This is needed because # some commands might take less than `cycle_time` (50ms) to complete, # so status would not even notice that the interp_state had changed and the # reset mode method would not be called. STATUS.old['interp_state'] = -1 if setTaskMode(linuxcnc.MODE_MDI): # issue multiple MDI commands separated by ';' for cmd in command.strip().split(';'): LOG.info("Issuing MDI command: %s", cmd) CMD.mdi(cmd) else: LOG.error("Failed to issue MDI command: {}".format(command))
def _issue_mdi_ok(mdi_cmd='', widget=None): if STAT.task_state == linuxcnc.STATE_ON \ and STATUS.allHomed() \ and STAT.interp_state == linuxcnc.INTERP_IDLE: ok = True msg = "" else: ok = False msg = "Can't issue MDI unless machine is ON, HOMED and IDLE" _issue_mdi_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _issue_mdi_bindOk(mdi_cmd='', widget=None): _issue_mdi_ok(mdi_cmd=mdi_cmd, widget=widget) STATUS.task_state.onValueChanged(lambda: _issue_mdi_ok(mdi_cmd=mdi_cmd, widget=widget)) STATUS.interp_state.onValueChanged(lambda: _issue_mdi_ok(mdi_cmd=mdi_cmd, widget=widget)) STATUS.homed.onValueChanged(lambda: _issue_mdi_ok(mdi_cmd=mdi_cmd, widget=widget)) issue_mdi.ok = _issue_mdi_ok issue_mdi.bindOk = _issue_mdi_bindOk # ------------------------------------------------------------------------- # WORK COORDINATES action # ------------------------------------------------------------------------- def set_work_coord(coord): issue_mdi(coord) def _set_work_coord_bindOk(coord='', widget=None): coord = coord.upper() _issue_mdi_bindOk(coord, widget=widget) if isinstance(widget, QComboBox): widget.setCurrentText(coord) widget.setCurrentText(STATUS.g5x_index.getString()) STATUS.g5x_index.notify(lambda g5x: widget.setCurrentText(g5x), 'string') else: widget.setCheckable(True) widget.setChecked(STATUS.g5x_index.getString() == coord) STATUS.g5x_index.notify(lambda g5x: widget.setChecked(g5x == coord), 'string') set_work_coord.ok = _issue_mdi_ok set_work_coord.bindOk = _set_work_coord_bindOk # ------------------------------------------------------------------------- # FEED HOLD action # -------------------------------------------------------------------------
[docs]class feedhold: """Feed Hold action Group""" # FIXME: Not sure what feedhold does, or how to turn it ON/OFF, if it even can be.
[docs] @staticmethod def enable(): """Enables Feed Hold""" LOG.info("Setting feedhold ENABLED") CMD.set_feed_hold(1)
[docs] @staticmethod def disable(): """Disables Feed Hold""" LOG.info("Setting feedhold DISABLED") CMD.set_feed_hold(0)
[docs] @staticmethod def toggle(): """Toggles Feed Hold state""" if STAT.feed_hold_enabled: feedhold.disable() else: feedhold.enable()
def _feed_hold_ok(widget=None): return STAT.task_state == linuxcnc.STATE_ON and STAT.interp_state == linuxcnc.INTERP_IDLE def _feed_hold_bindOk(widget): widget.setEnabled(STAT.task_state == linuxcnc.STATE_ON) widget.setChecked(STAT.feed_hold_enabled) STATUS.task_state.notify(lambda s: widget.setEnabled(s == linuxcnc.STATE_ON)) STATUS.feed_hold_enabled.notify(widget.setChecked) feedhold.enable.ok = feedhold.disable.ok = feedhold.toggle.ok = _feed_hold_ok feedhold.enable.bindOk = feedhold.disable.bindOk = feedhold.toggle.bindOk = _feed_hold_bindOk # ------------------------------------------------------------------------- # FEED OVERRIDE actions # -------------------------------------------------------------------------
[docs]class feed_override: """Feed Override Group"""
[docs] @staticmethod def enable(): """Feed Override Enable""" CMD.set_feed_override(True)
[docs] @staticmethod def disable(): """Feed Override Disable""" CMD.set_feed_override(False)
[docs] @staticmethod def toggle_enable(): """Feed Override Enable Toggle""" if STAT.feed_override_enabled: feed_override.disable() else: feed_override.enable()
[docs] @staticmethod def set(value): """Feed Override Set Value""" CMD.feedrate(float(value) / 100)
[docs] @staticmethod def reset(): """Feed Override Reset""" CMD.feedrate(1.0)
def _feed_override_enable_ok(widget=None): if STAT.task_state == linuxcnc.STATE_ON \ and STAT.interp_state == linuxcnc.INTERP_IDLE: ok = True msg = "" else: ok = False msg = "Machine must be ON and IDLE to enable/disable feed override" _feed_override_enable_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _feed_override_enable_bindOk(widget): STATUS.task_state.onValueChanged(lambda: _feed_override_enable_ok(widget)) STATUS.interp_state.onValueChanged(lambda: _feed_override_enable_ok(widget)) STATUS.feed_override_enabled.onValueChanged(widget.setChecked) def _feed_override_ok(value=100, widget=None): if STAT.task_state == linuxcnc.STATE_ON and STAT.feed_override_enabled == 1: ok = True msg = "" elif STAT.task_state != linuxcnc.STATE_ON: ok = False msg = "Machine must be ON to set feed override" elif STAT.feed_override_enabled == 0: ok = False msg = "Feed override is not enabled" else: ok = False msg = "Feed override can not be changed" _feed_override_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _feed_override_bindOk(value=100, widget=None): # This will work for any widget STATUS.task_state.onValueChanged(lambda: _feed_override_ok(widget=widget)) STATUS.feed_override_enabled.onValueChanged(lambda: _feed_override_ok(widget=widget)) try: # these will only work for QSlider or QSpinBox widget.setMinimum(0) widget.setMaximum(INFO.maxFeedOverride() * 100) try: widget.setSliderPosition(100) STATUS.feedrate.onValueChanged( lambda v: widget.setSliderPosition(v * 100)) except AttributeError: widget.setValue(100) STATUS.feedrate.onValueChanged( lambda v: widget.setValue(v * 100)) feed_override.set(100) except AttributeError: pass except: LOG.exception('Error in feed_override bindOk') feed_override.set.ok = feed_override.reset.ok = _feed_override_ok feed_override.set.bindOk = feed_override.reset.bindOk = _feed_override_bindOk feed_override.enable.ok = feed_override.disable.ok = feed_override.toggle_enable.ok = _feed_override_enable_ok feed_override.enable.bindOk = feed_override.disable.bindOk = feed_override.toggle_enable.bindOk = _feed_override_enable_bindOk # ------------------------------------------------------------------------- # RAPID OVERRIDE actions # -------------------------------------------------------------------------
[docs]class rapid_override: """Rapid Override Group""" @staticmethod def set(value): CMD.rapidrate(float(value) / 100) @staticmethod def reset(): CMD.rapidrate(1.0)
def _rapid_override_ok(value=100, widget=None): if STAT.task_state == linuxcnc.STATE_ON: ok = True msg = "" else: ok = False msg = "Machine must be ON to set rapid override" _rapid_override_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _rapid_override_bindOk(value=100, widget=None): # This will work for any widget STATUS.task_state.onValueChanged(lambda: _rapid_override_ok(widget=widget)) try: # these will only work for QSlider or QSpinBox widget.setMinimum(0) widget.setMaximum(100) try: widget.setSliderPosition(100) STATUS.rapidrate.onValueChanged( lambda v: widget.setSliderPosition(v * 100)) except AttributeError: STATUS.rapidrate.onValueChanged( lambda v: widget.setValue(v * 100)) widget.setValue(100) rapid_override.set(100) except AttributeError: pass except: LOG.exception('Error in rapid_override bindOk') rapid_override.set.ok = rapid_override.reset.ok = _rapid_override_ok rapid_override.set.bindOk = rapid_override.reset.bindOk = _rapid_override_bindOk # ------------------------------------------------------------------------- # MAX VEL OVERRIDE actions # -------------------------------------------------------------------------
[docs]class max_velocity: """Max Velocity Group"""
[docs] @staticmethod def set(value): """Max Velocity Override Set Value""" CMD.maxvel(float(value) / 60)
[docs] @staticmethod def reset(): """Max Velocity Override Reset Value""" CMD.maxvel(INFO.maxVelocity() / 60)
def _max_velocity_ok(value=100, widget=None): if STAT.task_state == linuxcnc.STATE_ON: ok = True msg = "" else: ok = False msg = "Machine must be ON to set max velocity" _max_velocity_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _max_velocity_bindOk(value=100, widget=None): # This will work for any widget STATUS.task_state.onValueChanged(lambda: _max_velocity_ok(widget=widget)) try: # these will only work for QSlider or QSpinBox widget.setMinimum(0) widget.setMaximum(INFO.maxVelocity()) try: widget.setSliderPosition(INFO.maxVelocity()) STATUS.max_velocity.onValueChanged( lambda v: widget.setSliderPosition(v * 60)) except AttributeError: widget.setValue(INFO.maxVelocity()) STATUS.max_velocity.onValueChanged( lambda v: widget.setValue(v * 60)) except AttributeError: pass except: LOG.exception('Error in max_velocity bindOk') max_velocity.set.ok = max_velocity.reset.ok = _max_velocity_ok max_velocity.set.bindOk = max_velocity.reset.bindOk = _max_velocity_bindOk # ------------------------------------------------------------------------- # set MODE actions # -------------------------------------------------------------------------
[docs]class mode: """Mode action group"""
[docs] @staticmethod def manual(): """Change mode to Manual ActionButton syntax: :: machine.mode.manual """ setTaskMode(linuxcnc.MODE_MANUAL)
[docs] @staticmethod def auto(): """Change mode to Auto ActionButton syntax: :: machine.mode.auto """ setTaskMode(linuxcnc.MODE_AUTO)
[docs] @staticmethod def mdi(): """Change mode to MDI ActionButton syntax: :: machine.mode.mdi """ setTaskMode(linuxcnc.MODE_MDI)
def _mode_ok(widget=None): if STAT.task_state == linuxcnc.STATE_ON and STAT.interp_state == linuxcnc.INTERP_IDLE: ok = True msg = "" else: ok = False msg = "Can't set mode when not ON and IDLE" _mode_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _manual_bindOk(widget): widget.setChecked(STAT.task_mode == linuxcnc.MODE_MANUAL) STATUS.task_state.onValueChanged(lambda: _mode_ok(widget)) STATUS.interp_state.onValueChanged(lambda: _mode_ok(widget)) STATUS.task_mode.onValueChanged(lambda m: widget.setChecked(m == linuxcnc.MODE_MANUAL)) mode.manual.ok = _mode_ok mode.manual.bindOk = _manual_bindOk def _auto_bindOk(widget): widget.setChecked(STAT.task_mode == linuxcnc.MODE_AUTO) STATUS.task_state.onValueChanged(lambda: _mode_ok(widget)) STATUS.interp_state.onValueChanged(lambda: _mode_ok(widget)) STATUS.task_mode.onValueChanged(lambda m: widget.setChecked(m == linuxcnc.MODE_AUTO)) mode.auto.ok = _mode_ok mode.auto.bindOk = _auto_bindOk def _mdi_bindOk(widget): widget.setChecked(STAT.task_mode == linuxcnc.MODE_MDI) STATUS.task_state.onValueChanged(lambda: _mode_ok(widget)) STATUS.interp_state.onValueChanged(lambda: _mode_ok(widget)) STATUS.task_mode.onValueChanged(lambda m: widget.setChecked(m == linuxcnc.MODE_MDI)) mode.mdi.ok = _mode_ok mode.mdi.bindOk = _mdi_bindOk # ------------------------------------------------------------------------- # HOME actions # -------------------------------------------------------------------------
[docs]class home: """Homing actions group"""
[docs] @staticmethod def all(): """Homes all axes ActionButton syntax:: machine.home.all """ LOG.info("Homing all axes") _home_joint(-1)
[docs] @staticmethod def axis(axis): """Home a specific axis Args: axis (int | str) : one of (xyzabcuvw or 012345678) ActionButton syntax to home the X axis:: machine.home.axis:x """ axis = getAxisLetter(axis) if axis.lower() == 'all': home.all() return jnum = INFO.COORDINATES.index(axis) LOG.info('Homing Axis: {}'.format(axis.upper())) _home_joint(jnum)
[docs] @staticmethod def joint(jnum): """Home a specific joint Args: jnum (int) : one of (012345678) ActionButton syntax to home joint 0:: machine.home.joint:0 """ LOG.info("Homing joint: {}".format(jnum)) _home_joint(jnum)
def _home_ok(jnum=-1, widget=None): # TODO: Check if homing a specific joint is OK if power.is_on(): # and not STAT.homed[jnum]: ok = True msg = "" else: ok = False msg = "Machine must be on to home" _home_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _home_all_bindOk(widget): STATUS.on.notify(lambda: _home_ok(widget=widget)) STATUS.homed.notify(lambda: _home_ok(widget=widget)) home.all.ok = _home_ok home.all.bindOk = _home_all_bindOk def _home_joint_bindOk(jnum, widget): STATUS.on.notify(lambda: _home_ok(jnum, widget=widget)) STATUS.homed.notify(lambda: _home_ok(jnum, widget=widget)) home.joint.ok = _home_ok home.joint.bindOk = _home_joint_bindOk def _home_axis_bindOk(axis, widget): aletter = getAxisLetter(axis) if aletter not in INFO.AXIS_LETTER_LIST: msg = 'Machine has no {} axis'.format(aletter.upper()) widget.setEnabled(False) widget.setToolTip(msg) widget.setStatusTip(msg) return jnum = INFO.AXIS_LETTER_LIST.index(axis) STATUS.on.notify(lambda: _home_ok(jnum, widget=widget)) home.axis.ok = _home_ok home.axis.bindOk = _home_axis_bindOk
[docs]class unhome: """Unhoming actions group"""
[docs] @staticmethod def all(): """Unhome all the axes ActionButton syntax:: machine.unhome.all """ LOG.info("Unhoming all Axes") _unhome_joint(-1)
[docs] @staticmethod def axis(axis): """Unhome a specific axis Args: axis (int | str) : one of (xyzabcuvw or 012345678) ActionButton syntax to unhome the X axis:: machine.unhome.axis:x """ axis = getAxisLetter(axis) if axis.lower() == 'all': # not sure what this is copied from home unhome.all() return jnum = INFO.COORDINATES.index(axis) LOG.info('Unhoming Axis: {}'.format(axis.upper())) _unhome_joint(jnum)
[docs] @staticmethod def joint(jnum): """Unhome a specific joint Args: jnum (int) : The number of the joint to home. ActionButton syntax to unhome the joint 0:: machine.unhome.joint:0 """ LOG.info("Unhoming joint: {}".format(jnum)) _unhome_joint(jnum)
def _home_joint(jnum): setTaskMode(linuxcnc.MODE_MANUAL) CMD.teleop_enable(False) CMD.home(jnum) def _unhome_joint(jnum): setTaskMode(linuxcnc.MODE_MANUAL) CMD.teleop_enable(False) CMD.unhome(jnum) # Homing helper functions
[docs]def getAxisLetter(axis): """Takes an axis letter or number and returns the axis letter. Args: axis (int | str) : Either a axis letter or an axis number. Returns: str : The axis letter, `all` for an input of -1. """ if isinstance(axis, int): return ['x', 'y', 'z', 'a', 'b', 'c', 'u', 'v', 'w', 'all'][axis] return axis.lower()
[docs]def getAxisNumber(axis): """Takes an axis letter or number and returns the axis number. Args: axis (int | str) : Either a axis letter or an axis number. Returns: int : The axis number, -1 for an input of `all`. """ if isinstance(axis, str): return ['x', 'y', 'z', 'a', 'b', 'c', 'u', 'v', 'w', 'all'].index(axis.lower()) return axis
# ------------------------------------------------------------------------- # OVERRIDE LIMITS action # ------------------------------------------------------------------------- def override_limits(): LOG.info("Setting override limits") CMD.override_limits() def _override_limits_ok(widget=None): ok = False mgs = None for anum in INFO.AXIS_NUMBER_LIST: if STAT.limit[anum] != 0: aaxis = getAxisLetter(anum) aletter = INFO.COORDINATES.index(aaxis) ok = True msg = "Axis {} on limit".format(aletter) if not ok: msg = "Axis must be on limit to override" _override_limits_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _override_limits_bindOk(widget): STATUS.limit.onValueChanged(lambda: _override_limits_ok(widget)) override_limits.ok = _override_limits_ok override_limits.bindOk = _override_limits_bindOk # ------------------------------------------------------------------------- # JOG actions # ------------------------------------------------------------------------- DEFAULT_JOG_SPEED = INFO.getJogVelocity() MAX_JOG_SPEED = INFO.getMaxJogVelocity() @setting('machine.jog.linear-speed', default_value=DEFAULT_JOG_SPEED, min_value=0, max_value=MAX_JOG_SPEED, persistent=True) def jog_linear_speed(obj): return obj.value @jog_linear_speed.setter def jog_linear_speed(obj, value): obj.value = obj.clampValue(value) jog_linear_speed.signal.emit(obj.value) LOG.debug("Setting jog linear speed: %4.1f", obj.value) percentage = int(obj.value * 100 / MAX_JOG_SPEED) jog_linear_speed_percentage.value = percentage jog_linear_speed_percentage.signal.emit(percentage) @setting('machine.jog.linear-speed-percentage', default_value=int(DEFAULT_JOG_SPEED * 100 / MAX_JOG_SPEED), min_value=0, max_value=100, persistent=False) def jog_linear_speed_percentage(obj): return obj.value @jog_linear_speed_percentage.setter def jog_linear_speed_percentage(obj, percentage): LOG.debug("Setting jog linear speed percentage: %d", percentage) jog_linear_speed.setValue(float(MAX_JOG_SPEED * percentage / 100)) @setting('machine.jog.angular-speed', default_value=100.0, persistent=False) def jog_angular_speed(obj): return obj.value @jog_angular_speed.setter def jog_angular_speed(obj, value): LOG.debug("Setting Jog Angular Speed: %d", value) obj.value = value @setting('machine.jog.mode-incremental', default_value=True) def jog_mode_incremental(obj): return obj.value @jog_mode_incremental.setter def jog_mode_incremental(obj, value): LOG.debug("Setting jog mode incremental: %s", value) obj.value = value def fromInternalLinearUnits(v, unit=None): if unit is None: unit = STAT.linear_units lu = (unit or 1) * 25.4 return v * lu def parseJogIncrement(jogincr): scale = 1 if isinstance(jogincr, basestring): jogincr = jogincr.lower() if jogincr.endswith("mm"): scale = fromInternalLinearUnits(1 / 25.4) elif jogincr.endswith("cm"): scale = fromInternalLinearUnits(10 / 25.4) elif jogincr.endswith("um"): scale = fromInternalLinearUnits(.001 / 25.4) elif jogincr.endswith("in") or jogincr.endswith("inch") or jogincr.endswith('"'): scale = fromInternalLinearUnits(1.) elif jogincr.endswith("mil"): scale = fromInternalLinearUnits(.001) else: scale = 1 jogincr = jogincr.rstrip(" inchmuil") try: if "/" in jogincr: p, q = jogincr.split("/") jogincr = float(p) / float(q) else: jogincr = float(jogincr) except ValueError: jogincr = 0 return jogincr * scale @setting('machine.jog.increment', parseJogIncrement(INFO.getIncrements()[0])) def jog_increment(obj): """Linear jog increment. Args: jogincr (str, int, float) : The desired jog increment. Can be passed as a string including an optional units specifier. Valid unit specifiers include ``mm``, ``cm``, ``um``, ``in``, ``inch``, ``"``, and ``mil``. """ return obj.value @jog_increment.setter def jog_increment(obj, jogincr): value = parseJogIncrement(jogincr) LOG.debug('Setting jog increment: %s (%2.4f)', jogincr, value) obj.value = value obj.signal.emit(value)
[docs]class jog: """Jog Actions Group""" max_linear_speed = INFO.getMaxJogVelocity() angular_speed = INFO.getJogVelocity() continuous = False increment = INFO.getIncrements()[0]
[docs] @staticmethod def axis(axis, direction=0, speed=None, distance=None): """Jog an axis. Action Button Syntax to jog the X axis in the positive direction:: machine.jog.axis:x,pos Args: axis (str | int) : Either the letter or number of the axis to jog. direction (str | int) : pos or +1 for positive, neg or -1 for negative. speed (float, optional) : Desired jog vel in machine_units/s. distance (float, optional) : Desired jog distance, continuous if 0.00. """ # check if it even makes sense to try to jog if STAT.task_state != linuxcnc.STATE_ON or STAT.task_mode != linuxcnc.MODE_MANUAL: return if isinstance(direction, str): direction = {'neg': -1, 'pos': 1}.get(direction.lower(), 0) axis = getAxisNumber(axis) # must be in teleoperating mode to jog. if STAT.motion_mode != linuxcnc.TRAJ_MODE_TELEOP: CMD.teleop_enable(1) if speed == 0 or direction == 0: CMD.jog(linuxcnc.JOG_STOP, 0, axis) else: if speed is None: if axis in (3, 4, 5): speed = jog_angular_speed.value / 60.0 else: speed = jog_linear_speed.value / 60.0 if distance is None: if jog_mode_incremental.value: distance = jog_increment.value else: distance = 0 velocity = float(speed) * direction if distance == 0: CMD.jog(linuxcnc.JOG_CONTINUOUS, 0, axis, velocity) else: CMD.jog(linuxcnc.JOG_INCREMENT, 0, axis, velocity, distance)
[docs] @staticmethod def set_jog_continuous(continuous): """Set Jog Continuous""" if continuous: LOG.debug("Setting jog mode to continuous") else: LOG.debug("Setting jog mode to incremental") jog.jog_mode_incremental = continuous
[docs] @staticmethod def set_increment(raw_increment): """Set Jog Increment""" jog_increment.setValue(raw_increment)
[docs] @staticmethod def set_linear_speed(speed): """Set Jog Linear Speed ActionSlider syntax:: machine.jog.set-linear-speed """ jog_linear_speed.setValue(float(speed))
[docs] @staticmethod def set_angular_speed(speed): """Set Jog Angular Speed""" jog_angular_speed.setValue(float(speed))
[docs] @staticmethod def set_linear_speed_percentage(percentage): """Set Jog Linear Speed Percentage""" jog_linear_speed_percentage.setValue(percentage)
def _jog_speed_slider_bindOk(widget): try: # these will only work for QSlider or QSpinBox widget.setMinimum(0) widget.setMaximum(100) widget.setValue((jog.linear_speed.getValue() / jog.max_linear_speed) * 100) # jog.linear_speed.connect(lambda v: widget.setValue(v * 100)) except AttributeError: pass jog.set_linear_speed.ok = jog.set_angular_speed.ok = lambda *a, **k: True jog.set_linear_speed.bindOk = jog.set_angular_speed.bindOk = lambda *a, **k: True jog.set_linear_speed_percentage.ok = lambda *a, **k: True jog.set_linear_speed_percentage.bindOk = _jog_speed_slider_bindOk def _jog_axis_ok(axis, direction=0, widget=None): axisnum = getAxisNumber(axis) if STAT.task_state == linuxcnc.STATE_ON \ and STAT.interp_state == linuxcnc.INTERP_IDLE \ and STAT.limit[axisnum] == 0: # and STAT.homed[axisnum] == 1 \ ok = True msg = "" else: ok = False msg = "Machine must be ON and in IDLE to jog" _jog_axis_ok.msg = msg if widget is not None: widget.setEnabled(ok) widget.setStatusTip(msg) widget.setToolTip(msg) return ok def _jog_axis_bindOk(axis, direction, widget): aletter = getAxisLetter(axis) if aletter not in INFO.AXIS_LETTER_LIST: msg = 'Machine has no {} axis'.format(aletter.upper()) widget.setEnabled(False) widget.setToolTip(msg) widget.setStatusTip(msg) return STATUS.limit.onValueChanged(lambda: _jog_axis_ok(aletter, direction, widget)) STATUS.homed.onValueChanged(lambda: _jog_axis_ok(aletter, direction, widget)) STATUS.task_state.onValueChanged(lambda: _jog_axis_ok(aletter, direction, widget)) STATUS.interp_state.onValueChanged(lambda: _jog_axis_ok(aletter, direction, widget)) jog.axis.ok = _jog_axis_ok jog.axis.bindOk = _jog_axis_bindOk
[docs]class jog_mode: """Jog Mode Group"""
[docs] @staticmethod def continuous(): """Set Jog Continuous ActionButton syntax:: machine.jog-mode.continuous """ jog_mode_incremental.setValue(False)
[docs] @staticmethod def incremental(): """Set Jog Incremental ActionButton syntax:: machine.jog-mode.incremental """ jog_mode_incremental.setValue(True)
[docs] @staticmethod def toggle(): """Jog Mode Toggle ActionButton syntax:: machine.jog-mode.toggle """ jog_mode_incremental.setValue(not jog_mode_incremental.value)
jog_mode.incremental.ok = jog_mode.continuous.ok = lambda *a, **kw: True jog_mode.incremental.bindOk = jog_mode.continuous.bindOk = lambda *a, **kw: True