diff --git a/configs/sim/axis/axis_halui_test.ini b/configs/sim/axis/axis_halui_test.ini
new file mode 100644
index 00000000000..877a91db9d8
--- /dev/null
+++ b/configs/sim/axis/axis_halui_test.ini
@@ -0,0 +1,225 @@
+# EMC controller parameters for a simulated machine.
+
+# General note: Comments can either be preceded with a # or ; - either is
+# acceptable, although # is in keeping with most linux config files.
+
+# General section -------------------------------------------------------------
+[EMC]
+
+# Version of this INI file
+VERSION = 1.1
+
+# Name of machine, for use with display, etc.
+MACHINE = LinuxCNC-HAL-SIM-AXIS
+
+# Debug level, 0 means no messages. See src/emc/nml_int/emcglb.h for others
+#DEBUG = 0x7FFFFFFF
+DEBUG = 0
+
+# Sections for display options ------------------------------------------------
+[DISPLAY]
+
+# Name of display program, e.g., axis
+DISPLAY = axis
+
+# Cycle time, in seconds, that display will sleep between polls
+CYCLE_TIME = 0.100
+
+# Path to help file
+HELP_FILE = doc/help.txt
+
+# Initial display setting for position, RELATIVE or MACHINE
+POSITION_OFFSET = RELATIVE
+
+# Initial display setting for position, COMMANDED or ACTUAL
+POSITION_FEEDBACK = ACTUAL
+
+# Highest value that will be allowed for feed override, 1.0 = 100%
+MAX_FEED_OVERRIDE = 1.2
+MAX_SPINDLE_OVERRIDE = 1.0
+
+MAX_LINEAR_VELOCITY = 5
+DEFAULT_LINEAR_VELOCITY = .25
+DEFAULT_SPINDLE_SPEED = 200
+# Prefix to be used
+PROGRAM_PREFIX = ../../nc_files/
+
+# Introductory graphic
+INTRO_GRAPHIC = linuxcnc.gif
+INTRO_TIME = 5
+
+#EDITOR = geany
+TOOL_EDITOR = tooledit
+
+INCREMENTS = 1 in, 0.1 in, 10 mil, 1 mil, 1mm, .1mm, 1/8000 in
+
+USER_COMMAND_FILE=gstatmessages.py
+
+[FILTER]
+PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
+PROGRAM_EXTENSION = .py Python Script
+
+png = image-to-gcode
+gif = image-to-gcode
+jpg = image-to-gcode
+py = python3
+
+# Task controller section -----------------------------------------------------
+[TASK]
+
+# Name of task controller program, e.g., milltask
+TASK = milltask
+
+# Cycle time, in seconds, that task controller will sleep between polls
+CYCLE_TIME = 0.001
+
+# Part program interpreter section --------------------------------------------
+[RS274NGC]
+
+# File containing interpreter variables
+PARAMETER_FILE = sim.var
+
+# Motion control section ------------------------------------------------------
+[EMCMOT]
+
+EMCMOT = motmod
+
+# Timeout for comm to emcmot, in seconds
+COMM_TIMEOUT = 1.0
+
+# BASE_PERIOD is unused in this configuration but specified in core_sim.hal
+BASE_PERIOD = 0
+# Servo task period, in nano-seconds
+SERVO_PERIOD = 1000000
+
+# section for main IO controller parameters -----------------------------------
+[EMCIO]
+# tool table file
+TOOL_TABLE = sim.tbl
+TOOL_CHANGE_POSITION = 0 0 0
+TOOL_CHANGE_QUILL_UP = 1
+
+# Hardware Abstraction Layer section --------------------------------------------------
+[HAL]
+
+# The run script first uses halcmd to execute any HALFILE
+# files, and then to execute any individual HALCMD commands.
+#
+
+# list of hal config files to run through halcmd
+# files are executed in the order in which they appear
+HALFILE = core_sim.hal
+HALFILE = sim_spindle_encoder.hal
+HALFILE = axis_manualtoolchange.hal
+HALFILE = simulated_home.hal
+HALFILE = check_xyz_constraints.hal
+HALFILE = cooling.hal
+
+# list of halcmd commands to execute
+# commands are executed in the order in which they appear
+#HALCMD = save neta
+
+# Single file that is executed after the GUI has started. Only supported by
+# AXIS at this time (only AXIS creates a HAL component of its own)
+#POSTGUI_HALFILE = test_postgui.hal
+POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel
+
+HALUI = halui
+
+# Trajectory planner section --------------------------------------------------
+[TRAJ]
+COORDINATES = X Y Z
+LINEAR_UNITS = inch
+ANGULAR_UNITS = degree
+MAX_LINEAR_VELOCITY = 4
+DEFAULT_LINEAR_ACCELERATION = 100
+MAX_LINEAR_ACCELERATION = 100
+POSITION_FILE = position.txt
+
+[KINS]
+KINEMATICS = trivkins
+JOINTS = 3
+
+# Axes sections ---------------
+[AXIS_X]
+MAX_VELOCITY = 4
+MAX_ACCELERATION = 100.0
+MIN_LIMIT = -10.0
+MAX_LIMIT = 10.0
+
+[AXIS_Y]
+MAX_VELOCITY = 4
+MAX_ACCELERATION = 100.0
+MIN_LIMIT = -10.0
+MAX_LIMIT = 10.0
+
+[AXIS_Z]
+MAX_VELOCITY = 4
+MAX_ACCELERATION = 100.0
+MIN_LIMIT = -8.0
+MAX_LIMIT = 0.12
+
+# Joints sections -------------
+[JOINT_0]
+TYPE = LINEAR
+HOME = 0.000
+MAX_VELOCITY = 5
+MAX_ACCELERATION = 50.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+FERROR = 0.050
+MIN_FERROR = 0.010
+MIN_LIMIT = -10.0
+MAX_LIMIT = 10.0
+HOME_OFFSET = 0.0
+HOME_SEARCH_VEL = 20.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 1
+HOME_IS_SHARED = 1
+
+[JOINT_1]
+TYPE = LINEAR
+HOME = 0.000
+MAX_VELOCITY = 5
+MAX_ACCELERATION = 50.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+FERROR = 0.050
+MIN_FERROR = 0.010
+MIN_LIMIT = -10.0
+MAX_LIMIT = 10.0
+HOME_OFFSET = 0.0
+HOME_SEARCH_VEL = 20.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 1
+
+[JOINT_2]
+TYPE = LINEAR
+HOME = 0.0
+MAX_VELOCITY = 5
+MAX_ACCELERATION = 50.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+MIN_LIMIT = -8.0
+
+# Normally the Z max should be 0.000!
+# The only reason it's greater than 0 here is so that the splash screen
+# gcode will run.
+MAX_LIMIT = 0.12
+
+FERROR = 0.050
+MIN_FERROR = 0.010
+HOME_OFFSET = 1.0
+HOME_SEARCH_VEL = 20.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 0
+HOME_IS_SHARED = 1
diff --git a/configs/sim/axis/gstatmessages.py b/configs/sim/axis/gstatmessages.py
new file mode 100644
index 00000000000..5c9e1e95d16
--- /dev/null
+++ b/configs/sim/axis/gstatmessages.py
@@ -0,0 +1,66 @@
+
+from hal_glib import GStat
+GSTAT = GStat()
+GSTAT.forced_update()
+GSTAT.connect('jograte-changed', lambda w, data: vars.jog_speed.set(data))
+GSTAT.connect('axis-selection-changed', lambda w,data: select_axis(data))
+GSTAT.connect('cycle-start-request', lambda w, state : cycle_start_request(state))
+GSTAT.connect('cycle-pause-request', lambda w, state: pause_request(state))
+GSTAT.connect('ok-request', lambda w, state: dialog_ext_control(w,1,1))
+GSTAT.connect('cancel-request', lambda w, state: dialog_ext_control(w,1,0))
+GSTAT.connect('macro-call-request', lambda w, name: request_macro_call(name))
+def user_live_update():
+ GSTAT.run_iteration()
+
+def select_axis(data):
+ if data is None: return
+ widget = getattr(widgets, "axis_%s" % data.lower())
+ widget.focus()
+ widget.invoke()
+
+def cycle_start_request(state):
+ print('cycle start',state)
+ commands.task_run(None)
+
+def pause_request(state):
+ print('cycle pause',state)
+ commands.task_pauseresume(None)
+
+def dialog_ext_control(widget,t,state):
+ print('dialog control',widget,state)
+
+ flag = False
+ for child in root_window.winfo_children():
+ #print(child)
+ if isinstance(child, Tkinter.Toplevel):
+ #print(f"Found a Toplevel window: {child}")
+ if '.!toplevel' in str(child):
+ #print('sending command:',child)
+ for child2 in child.winfo_children():
+ #print(child2)
+ if isinstance(child2, Tkinter.Frame):
+ for child3 in child2.winfo_children():
+ #print(child3)
+ if isinstance(child3, Tkinter.Button):
+ #print(dir(child3))
+ txt = child3.cget("text")
+ if txt.lower() == 'ok' and state:
+ #print('Ok')
+ child3.invoke()
+ flag = True
+ break
+ elif txt.lower() == 'cancel' and not state:
+ #print('Cancel')
+ child3.invoke()
+ flag = True
+ break
+ if flag: break
+ if flag: break
+ else:
+ #print('No window')
+ # remove one error message
+ if state == 0:
+ notifications.clear_one()
+
+def request_macro_call(name):
+ print('request macro:',name)
diff --git a/configs/sim/axis/panel.hal b/configs/sim/axis/panel.hal
new file mode 100644
index 00000000000..5db03ed71da
--- /dev/null
+++ b/configs/sim/axis/panel.hal
@@ -0,0 +1,24 @@
+net rate halui.axis.jog-speed panel.jog-rate
+
+net sx halui.axis.x.select panel.axis-x
+net sy halui.axis.y.select panel.axis-y
+net sz halui.axis.z.select panel.axis-z
+
+net jog-p halui.axis.selected.plus panel.jog-pos
+net jog-m halui.axis.selected.minus panel.jog-neg
+
+#net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0
+#net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1
+#net m2 halui.gui.mdi-command-0 panel.mdi-2
+
+net man panel.manual-mode halui.mode.manual
+net mdi panel.mdi-mode halui.mode.mdi
+net auto panel.auto-mode halui.mode.auto
+
+net pause halui.cycle.start panel.cycle-start
+net start halui.cycle.pause panel.cycle-pause
+net abort halui.abort panel.cycle-abort
+
+net cancel halui.gui.cancel panel.cancel
+net ok halui.gui.ok panel.ok
+
diff --git a/configs/sim/axis/panel.ui b/configs/sim/axis/panel.ui
new file mode 100644
index 00000000000..7264d58f6d0
--- /dev/null
+++ b/configs/sim/axis/panel.ui
@@ -0,0 +1,365 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 404
+ 560
+
+
+
+ MainWindow
+
+
+
+ -
+
+
-
+
+
+ Axis Selection
+
+
+
-
+
+
+ None
+
+
+ true
+
+
+ true
+
+
+ axis-none
+
+
+
+ -
+
+
+ X
+
+
+ true
+
+
+ true
+
+
+ axis-x
+
+
+
+ -
+
+
+ Y
+
+
+ true
+
+
+ true
+
+
+ axis-y
+
+
+
+ -
+
+
+ Z
+
+
+ true
+
+
+ true
+
+
+ axis-z
+
+
+
+
+
+
+ -
+
+
+ Axis Jog
+
+
+
-
+
+
+ +
+
+
+ jog-pos
+
+
+
+ -
+
+
+ -
+
+
+ jog-neg
+
+
+
+
+
+
+ -
+
+
+ jog rate
+
+
+
-
+
+
+ Qt::Horizontal
+
+
+ jog-rate
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ MDI Comands
+
+
+
-
+
+
+ 0
+
+
+ mdi-0
+
+
+
+ -
+
+
+ 1
+
+
+ mdi-1
+
+
+
+ -
+
+
+ 2
+
+
+ mdi-2
+
+
+
+
+
+
+ -
+
+
+ Mode Comands
+
+
+
-
+
+
+ Manual
+
+
+ false
+
+
+ true
+
+
+ manual-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ MDI
+
+
+ false
+
+
+ true
+
+
+ mdi-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Auto
+
+
+ false
+
+
+ true
+
+
+ auto-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ program control
+
+
+
-
+
+
+ start
+
+
+ cycle-start
+
+
+
+ -
+
+
+ pause
+
+
+ cycle-pause
+
+
+
+ -
+
+
+ Abort
+
+
+ cycle-abort
+
+
+
+
+
+
+ -
+
+
+ dialog control
+
+
+
-
+
+
+ ok
+
+
+ ok
+
+
+
+ -
+
+
+ cancel
+
+
+ cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ StatusSlider
+ QSlider
+ qtvcp.widgets.status_slider
+
+
+
+
+
diff --git a/configs/sim/gmoccapy/gmoccapy_halui_test.ini b/configs/sim/gmoccapy/gmoccapy_halui_test.ini
new file mode 100644
index 00000000000..27579c0b389
--- /dev/null
+++ b/configs/sim/gmoccapy/gmoccapy_halui_test.ini
@@ -0,0 +1,225 @@
+# EMC controller parameters for a simulated machine.
+# General note: Comments can either be preceded with a # or ; - either is
+# acceptable, although # is in keeping with most linux config files.
+
+# General section -------------------------------------------------------------
+[EMC]
+VERSION = 1.1
+MACHINE = gmoccapy
+DEBUG = 0
+
+# Sections for display options ------------------------------------------------
+[DISPLAY]
+DISPLAY = gmoccapy -i
+# Log level:
+# DEBUG -d
+# INFO -i
+# VERBOSE -v
+# ERROR -q
+
+# Cycle time, in milliseconds, that display will sleep between polls
+CYCLE_TIME = 100
+
+# Values that will be allowed for override, 1.0 = 100%
+MAX_FEED_OVERRIDE = 1.5
+MAX_SPINDLE_OVERRIDE = 1.2
+MIN_SPINDLE_OVERRIDE = 0.5
+
+# Initial value for spindle speed
+DEFAULT_SPINDLE_SPEED = 450
+
+# The following are not used, added here to suppress warnings (from qt_istat/logger).
+DEFAULT_LINEAR_VELOCITY = 35
+MIN_LINEAR_VELOCITY = 0
+MAX_LINEAR_VELOCITY = 234
+DEFAULT_SPINDLE_0_SPEED = 500
+MIN_SPINDLE_0_SPEED = 0
+MAX_SPINDLE_0_SPEED = 3000
+MAX_SPINDLE_0_OVERRIDE = 1.2
+MIN_SPINDLE_0_OVERRIDE = 0.5
+
+# Prefix to be used
+PROGRAM_PREFIX = ../../nc_files/
+
+# Introductory graphic
+INTRO_GRAPHIC = linuxcnc.gif
+INTRO_TIME = 5
+
+# list of selectable jog increments
+INCREMENTS = 1.000 mm, 0.100 mm, 0.010 mm, 0.001 mm, 1.2345 inch
+
+# for details see nc_files/subroutines/maco_instructions.txt
+[FILTER]
+PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
+PROGRAM_EXTENSION = .py Python Script
+png = image-to-gcode
+gif = image-to-gcode
+jpg = image-to-gcode
+py = python3
+
+# Task controller section -----------------------------------------------------
+[RS274NGC]
+RS274NGC_STARTUP_CODE = G17 G21 G40 G43H0 G54 G64P0.005 G80 G90 G94 G97 M5 M9
+PARAMETER_FILE = sim.var
+SUBROUTINE_PATH = ./macros
+REMAP=M6 modalgroup=6 prolog=change_prolog ngc=change_g43 epilog=change_epilog
+REMAP=M61 modalgroup=6 prolog=settool_prolog ngc=settool_g43 epilog=settool_epilog
+
+# the Python plugins serves interpreter and task
+[PYTHON]
+PATH_PREPEND = ./python
+TOPLEVEL = ./python/toplevel.py
+LOG_LEVEL = 0
+
+# Motion control section ------------------------------------------------------
+[EMCMOT]
+EMCMOT = motmod
+COMM_TIMEOUT = 1.0
+BASE_PERIOD = 100000
+SERVO_PERIOD = 1000000
+
+# Hardware Abstraction Layer section --------------------------------------------------
+[TASK]
+TASK = milltask
+CYCLE_TIME = 0.001
+
+# Part program interpreter section --------------------------------------------
+[HAL]
+HALFILE = core_sim.hal
+HALFILE = spindle_sim.hal
+HALFILE = simulated_home.hal
+
+# Single file that is executed after the GUI has started.
+POSTGUI_HALFILE = gmoccapy_postgui.hal
+POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel
+
+HALUI = halui
+
+# Trajectory planner section --------------------------------------------------
+[HALUI]
+#No Content
+
+[MDI_COMMAND_LIST]
+# for macro buttons on main oage up to 10 possible
+MDI_COMMAND_MACRO0 = G0 Z1;X0 Y0;Z0, Goto\nUser\nZero
+MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
+MDI_COMMAND_MACRO2 = (MSG, macro 2);
+MDI_COMMAND_MACRO3 = (MSG, macro 2)
+MDI_COMMAND_MACRO4 = (MSG, macro 2),test
+[TRAJ]
+COORDINATES = X Y Z
+LINEAR_UNITS = mm
+ANGULAR_UNITS = degree
+DEFAULT_LINEAR_VELOCITY = 35
+MAX_LINEAR_VELOCITY = 234
+POSITION_FILE = position.txt
+#NO_FORCE_HOMING = 1
+
+[EMCIO]
+# tool table file
+TOOL_TABLE = tool.tbl
+TOOL_CHANGE_POSITION = 100 100 -10
+TOOL_CHANGE_QUILL_UP = 1
+
+[KINS]
+KINEMATICS = trivkins coordinates=xyz
+JOINTS = 3
+
+[AXIS_X]
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+
+[JOINT_0]
+TYPE = LINEAR
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+FERROR = 0.050
+MIN_FERROR = 0.010
+HOME_OFFSET = 0.0
+HOME = 10
+HOME_SEARCH_VEL = 200.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 1
+HOME_IS_SHARED = 1
+
+# Second axis
+[AXIS_Y]
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+
+[JOINT_1]
+TYPE = LINEAR
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+FERROR = 0.050
+MIN_FERROR = 0.010
+HOME_OFFSET = 0.0
+HOME = 10
+HOME_SEARCH_VEL = 200.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 1
+
+# Third axis
+[AXIS_Z]
+MIN_LIMIT = -400.0
+MAX_LIMIT = 0.001
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+
+[JOINT_2]
+TYPE = LINEAR
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+MIN_LIMIT = -400.0
+MAX_LIMIT = 0.001
+FERROR = 0.050
+MIN_FERROR = 0.010
+HOME_OFFSET = 1.0
+HOME = -10
+HOME_SEARCH_VEL = 200.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 0
+HOME_IS_SHARED = 1
+
+# section for main IO controller parameters -----------------------------------
+[MACROS]
+MACRO = go_to_position x-pos y-pos z-pos
+MACRO = i_am_lost
+MACRO = increment x-incr y-incr
+MACRO = macro_4
+MACRO = macro_5
+MACRO = macro_6
+MACRO = macro_7
+MACRO = macro_8
+MACRO = macro_9
+MACRO = macro_10
+MACRO = macro_11
+MACRO = macro_12
+MACRO = macro_13
+MACRO = macro_14
+MACRO = macro_15
+
+
diff --git a/configs/sim/gmoccapy/gmoccapy_halui_test_4_axis.ini b/configs/sim/gmoccapy/gmoccapy_halui_test_4_axis.ini
new file mode 100644
index 00000000000..f722d2e6654
--- /dev/null
+++ b/configs/sim/gmoccapy/gmoccapy_halui_test_4_axis.ini
@@ -0,0 +1,218 @@
+# General section -------------------------------------------------------------
+[EMC]
+VERSION = 1.1
+MACHINE = gmoccapy 4 axis
+DEBUG = 0
+
+# for details see nc_files/subroutines/maco_instructions.txt
+[DISPLAY]
+DISPLAY = gmoccapy
+
+# Cycle time, in milliseconds, that display will sleep between polls
+CYCLE_TIME = 100
+
+# Highest value that will be allowed for feed override, 1.0 = 100%
+MAX_FEED_OVERRIDE = 1.5
+MAX_SPINDLE_OVERRIDE = 1.2
+MIN_SPINDLE_OVERRIDE = .5
+
+# Prefix to be used
+PROGRAM_PREFIX = ../../nc_files/
+
+# Introductory graphic
+INTRO_GRAPHIC = linuxcnc.gif
+INTRO_TIME = 5
+
+# list of selectable jog increments
+INCREMENTS = 1.000 mm, 0.100 mm, 0.010 mm, 0.001 mm, 90.000 °
+
+[FILTER]
+PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
+PROGRAM_EXTENSION = .py Python Script
+png = image-to-gcode
+gif = image-to-gcode
+jpg = image-to-gcode
+py = python3
+
+# Task controller section -----------------------------------------------------
+[RS274NGC]
+RS274NGC_STARTUP_CODE = G17 G21 G40 G43H0 G54 G64P0.005 G80 G90 G94 G97 M5 M9
+PARAMETER_FILE = sim.var
+SUBROUTINE_PATH = ./macros
+REMAP=M6 modalgroup=6 prolog=change_prolog ngc=change_g43 epilog=change_epilog
+REMAP=M61 modalgroup=6 prolog=settool_prolog ngc=settool_g43 epilog=settool_epilog
+
+# the Python plugins serves interpreter and task
+[PYTHON]
+PATH_PREPEND = ./python
+TOPLEVEL = ./python/toplevel.py
+LOG_LEVEL = 0
+
+# Motion control section ------------------------------------------------------
+[EMCMOT]
+EMCMOT = motmod
+COMM_TIMEOUT = 1.0
+BASE_PERIOD = 100000
+SERVO_PERIOD = 1000000
+
+# Hardware Abstraction Layer section --------------------------------------------------
+[TASK]
+TASK = milltask
+CYCLE_TIME = 0.001
+
+# Part program interpreter section --------------------------------------------
+[HAL]
+HALFILE = core_sim4.hal
+HALFILE = spindle_sim.hal
+HALFILE = simulated_home.hal
+
+# Single file that is executed after the GUI has started.
+POSTGUI_HALFILE = gmoccapy_postgui.hal
+POSTGUI_HALCMD = loadusr qtvcp -a -H panel-4axis.hal panel
+HALUI = halui
+
+# Trajectory planner section --------------------------------------------------
+[HALUI]
+#No Content
+[MDI_COMMAND_LIST]
+# for macro buttons on main oage up to 10 possible
+MDI_COMMAND_MACRO0 = G0 Z1;X0 Y0;Z0, Goto\nUser\nZero
+MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
+MDI_COMMAND_MACRO2 = (MSG, macro 2);
+MDI_COMMAND_MACRO3 = (MSG, macro 2)
+MDI_COMMAND_MACRO4 = (MSG, macro 2),test
+
+
+[TRAJ]
+COORDINATES = X Y Z C
+LINEAR_UNITS = mm
+ANGULAR_UNITS = degree
+DEFAULT_LINEAR_VELOCITY = 50
+MAX_LINEAR_VELOCITY = 234
+POSITION_FILE = position.txt
+
+# First axis = X
+[EMCIO]
+# tool table file
+TOOL_TABLE = tool.tbl
+TOOL_CHANGE_POSITION = 100 100 -10
+TOOL_CHANGE_QUILL_UP = 1
+
+[KINS]
+KINEMATICS = trivkins coordinates=xyzc
+JOINTS = 4
+
+[AXIS_X]
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+
+[JOINT_0]
+TYPE = LINEAR
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+FERROR = 0.050
+MIN_FERROR = 0.010
+HOME_OFFSET = 0.0
+HOME = 10
+HOME_SEARCH_VEL = 200.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 1
+HOME_IS_SHARED = 1
+
+# Second axis = Y
+[AXIS_Y]
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+
+[JOINT_1]
+TYPE = LINEAR
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+MIN_LIMIT = -400.0
+MAX_LIMIT = 400.0
+FERROR = 0.050
+MIN_FERROR = 0.010
+HOME_OFFSET = 0.0
+HOME = 10
+HOME_SEARCH_VEL = 200.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 1
+HOME_IS_SHARED = 1
+
+# Third axis = Z
+[AXIS_Z]
+MIN_LIMIT = -400.0
+MAX_LIMIT = 0.001
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+
+[JOINT_2]
+TYPE = LINEAR
+MAX_VELOCITY = 166
+MAX_ACCELERATION = 1500.0
+BACKLASH = 0.000
+INPUT_SCALE = 4000
+OUTPUT_SCALE = 1.000
+MIN_LIMIT = -400.0
+MAX_LIMIT = 0.001
+FERROR = 0.050
+MIN_FERROR = 0.010
+HOME_OFFSET = 1.0
+HOME = -10
+HOME_SEARCH_VEL = 200.0
+HOME_LATCH_VEL = 20.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 0
+HOME_IS_SHARED = 1
+
+# Fourth axis = A
+# Fifth axis = B
+
+# Sixt axis = C
+[AXIS_C]
+MAX_VELOCITY = 90.0
+MAX_ACCELERATION = 1200.0
+
+[JOINT_3]
+TYPE = ANGULAR
+HOME = 0.0
+MAX_VELOCITY = 90.0
+MAX_ACCELERATION = 1200.0
+BACKLASH = 0.000
+INPUT_SCALE = 40
+OUTPUT_SCALE = 1.000
+FERROR = 5.0
+MIN_FERROR = 1.0
+HOME_OFFSET = 0.0
+HOME_SEARCH_VEL = 0.0
+HOME_LATCH_VEL = 0.0
+HOME_USE_INDEX = NO
+HOME_IGNORE_LIMITS = NO
+HOME_SEQUENCE = 1
+
+# section for main IO controller parameters -----------------------------------
+[MACROS]
+MACRO = i_am_lost
+MACRO = halo_world
+MACRO = jog_around
+MACRO = increment xinc yinc
+MACRO = go_to_position X-pos Y-pos Z-pos
+
+# Sections for display options ------------------------------------------------
diff --git a/configs/sim/gmoccapy/gmoccapy_right_panel.ini b/configs/sim/gmoccapy/gmoccapy_right_panel.ini
index 62213d687d5..01312384c08 100644
--- a/configs/sim/gmoccapy/gmoccapy_right_panel.ini
+++ b/configs/sim/gmoccapy/gmoccapy_right_panel.ini
@@ -31,6 +31,17 @@ INTRO_TIME = 5
# list of selectable jog increments
INCREMENTS = 1mm, 0.1mm, 0.01mm, 0.001mm, 1.2345in
+MACRO = i_am_lost
+MACRO = halo_world
+MACRO = jog_around
+MACRO = increment xinc yinc
+MACRO = go_to_position X-pos Y-pos Z-pos
+
+[MDI_COMMAND_LIST]
+# for macro buttons on main oage up to 10 possible
+MDI_COMMAND = G0 Z1;X0 Y0;Z0, Goto\nUser\nZero
+MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
+
[FILTER]
PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
PROGRAM_EXTENSION = .py Python Script
@@ -75,7 +86,7 @@ HALFILE = simulated_home.hal
POSTGUI_HALFILE = gmoccapy_postgui.hal
HALUI = halui
-
+HALBRIDGE = hal_bridge -d
# Trajectory planner section --------------------------------------------------
[HALUI]
#No Content
diff --git a/configs/sim/gmoccapy/panel-4axis.hal b/configs/sim/gmoccapy/panel-4axis.hal
new file mode 100644
index 00000000000..95e7d4fdf6c
--- /dev/null
+++ b/configs/sim/gmoccapy/panel-4axis.hal
@@ -0,0 +1,65 @@
+net rate halui.axis.jog-speed panel.jog-rate
+net angular-rate halui.axis.jog-speed-angular panel.jog-rate-angular
+
+net sx halui.axis.x.select panel.axis-x
+net sy halui.axis.y.select panel.axis-y
+net sz halui.axis.z.select panel.axis-z
+net sc halui.axis.c.select panel.axis-c
+
+net sx axis.x.jog-enable
+net sy axis.y.jog-enable
+net sz axis.z.jog-enable
+net sc axis.c.jog-enable
+net sgui halui.mpg-select.0 panel.select-gui0
+
+net jog-p halui.axis.selected.plus panel.jog-pos
+net jog-m halui.axis.selected.minus panel.jog-neg
+
+net mpg-scale axis.x.jog-scale panel.mpg-scale
+net mpg-scale axis.y.jog-scale
+net mpg-scale axis.z.jog-scale
+net mpg-scale axis.c.jog-scale
+
+net mpg-count panel.mpg-wheel-s
+net mpg-count gmoccapy.mpg-in
+net mpg-count axis.x.jog-counts
+net mpg-count axis.y.jog-counts
+net mpg-count axis.z.jog-counts
+net mpg-count axis.c.jog-counts
+
+net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0
+net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1
+net m2 halui.gui.mdi-command-MACRO2 panel.mdi-2
+
+net man panel.manual-mode halui.mode.manual
+net mdi panel.mdi-mode halui.mode.mdi
+net auto panel.auto-mode halui.mode.auto
+
+net pause halui.cycle.start panel.cycle-start
+net start halui.cycle.pause panel.cycle-pause
+net abort halui.abort panel.cycle-abort
+
+net cancel halui.gui.cancel panel.cancel
+net ok halui.gui.ok panel.ok
+
+
+net softkey0 halui.gui.softkey-0 panel.softkey-v0
+net softkey1 halui.gui.softkey-1 panel.softkey-v1
+net softkey2 halui.gui.softkey-2 panel.softkey-v2
+net softkey3 halui.gui.softkey-3 panel.softkey-v3
+net softkey4 halui.gui.softkey-4 panel.softkey-v4
+net softkey5 halui.gui.softkey-5 panel.softkey-v5
+net softkey6 halui.gui.softkey-6 panel.softkey-v6
+net softkeyh0 halui.gui.softkey-10 panel.softkey-h0
+net softkeyh1 halui.gui.softkey-11 panel.softkey-h1
+net softkeyh2 halui.gui.softkey-12 panel.softkey-h2
+net softkeyh3 halui.gui.softkey-13 panel.softkey-h3
+net softkeyh4 halui.gui.softkey-14 panel.softkey-h4
+net softkeyh5 halui.gui.softkey-15 panel.softkey-h5
+net softkeyh6 halui.gui.softkey-16 panel.softkey-h6
+net softkeyh7 halui.gui.softkey-17 panel.softkey-h7
+net softkeyh8 halui.gui.softkey-18 panel.softkey-h8
+net softkeyh9 halui.gui.softkey-19 panel.softkey-h9
+
+net exit halui.gui.shutdown panel.exit
+net reload halui.gui.reload-display panel.reload
diff --git a/configs/sim/gmoccapy/panel.hal b/configs/sim/gmoccapy/panel.hal
new file mode 100644
index 00000000000..3be61d9baf4
--- /dev/null
+++ b/configs/sim/gmoccapy/panel.hal
@@ -0,0 +1,59 @@
+net rate halui.axis.jog-speed panel.jog-rate
+
+net sx halui.axis.x.select panel.axis-x
+net sy halui.axis.y.select panel.axis-y
+net sz halui.axis.z.select panel.axis-z
+
+net sx axis.x.jog-enable
+net sy axis.y.jog-enable
+net sz axis.z.jog-enable
+net sgui halui.mpg-select.0 panel.select-gui0
+
+net jog-p halui.axis.selected.plus panel.jog-pos
+net jog-m halui.axis.selected.minus panel.jog-neg
+
+net mpg-scale axis.x.jog-scale panel.mpg-scale
+net mpg-scale axis.y.jog-scale
+net mpg-scale axis.z.jog-scale
+
+net mpg-count panel.mpg-wheel-s
+net mpg-count gmoccapy.mpg-in
+net mpg-count axis.x.jog-counts
+net mpg-count axis.y.jog-counts
+net mpg-count axis.z.jog-counts
+
+net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0
+net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1
+net m2 halui.gui.mdi-command-MACRO2 panel.mdi-2
+
+net man panel.manual-mode halui.mode.manual
+net mdi panel.mdi-mode halui.mode.mdi
+net auto panel.auto-mode halui.mode.auto
+
+net pause halui.cycle.start panel.cycle-start
+net start halui.cycle.pause panel.cycle-pause
+net abort halui.abort panel.cycle-abort
+
+net cancel halui.gui.cancel panel.cancel
+net ok halui.gui.ok panel.ok
+
+net softkey0 halui.gui.softkey-0 panel.softkey-v0
+net softkey1 halui.gui.softkey-1 panel.softkey-v1
+net softkey2 halui.gui.softkey-2 panel.softkey-v2
+net softkey3 halui.gui.softkey-3 panel.softkey-v3
+net softkey4 halui.gui.softkey-4 panel.softkey-v4
+net softkey5 halui.gui.softkey-5 panel.softkey-v5
+net softkey6 halui.gui.softkey-6 panel.softkey-v6
+net softkeyh0 halui.gui.softkey-10 panel.softkey-h0
+net softkeyh1 halui.gui.softkey-11 panel.softkey-h1
+net softkeyh2 halui.gui.softkey-12 panel.softkey-h2
+net softkeyh3 halui.gui.softkey-13 panel.softkey-h3
+net softkeyh4 halui.gui.softkey-14 panel.softkey-h4
+net softkeyh5 halui.gui.softkey-15 panel.softkey-h5
+net softkeyh6 halui.gui.softkey-16 panel.softkey-h6
+net softkeyh7 halui.gui.softkey-17 panel.softkey-h7
+net softkeyh8 halui.gui.softkey-18 panel.softkey-h8
+net softkeyh9 halui.gui.softkey-19 panel.softkey-h9
+
+net exit halui.gui.shutdown panel.exit
+net reload halui.gui.reload-display panel.reload
diff --git a/configs/sim/gmoccapy/panel.ui b/configs/sim/gmoccapy/panel.ui
new file mode 100644
index 00000000000..cf614f0bb7f
--- /dev/null
+++ b/configs/sim/gmoccapy/panel.ui
@@ -0,0 +1,1407 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 874
+ 756
+
+
+
+ MainWindow
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+ ESTOP
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ 255
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ 0.300000000000000
+
+
+ 10
+
+
+ 0
+
+
+ 0
+
+
+ 5.000000000000000
+
+
+ 0.300000000000000
+
+
+ 0.900000000000000
+
+
+ True
+
+
+ False
+
+
+ print("true command")
+
+
+ print("false command")
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ 0
+
+
+ true
+
+
+ false
+
+
+ -1
+
+
+ 0.010000000000000
+
+
+ 0.025000000000000
+
+
+ -1.000000000000000
+
+
+ false
+
+
+ 0.300000000000000
+
+
+ 50.000000000000000
+
+
+ P
+
+
+
+
+
+ 0
+
+
+ %1.3f in
+
+
+ %1.2f mm
+
+
+
+ -
+
+
+ ON
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ 255
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ 0.300000000000000
+
+
+ 10
+
+
+ 0
+
+
+ 0
+
+
+ 5.000000000000000
+
+
+ 0.300000000000000
+
+
+ 0.900000000000000
+
+
+ True
+
+
+ False
+
+
+ print("true command")
+
+
+ print("false command")
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ 0
+
+
+ true
+
+
+ false
+
+
+ -1
+
+
+ 0.010000000000000
+
+
+ 0.025000000000000
+
+
+ -1.000000000000000
+
+
+ false
+
+
+ 0.300000000000000
+
+
+ 50.000000000000000
+
+
+ P
+
+
+
+
+
+ 0
+
+
+ %1.3f in
+
+
+ %1.2f mm
+
+
+
+ -
+
+
+ HOME
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ 255
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ 0.300000000000000
+
+
+ 10
+
+
+ 0
+
+
+ 0
+
+
+ 5.000000000000000
+
+
+ 0.300000000000000
+
+
+ 0.900000000000000
+
+
+ True
+
+
+ False
+
+
+ print("true command")
+
+
+ print("false command")
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ 0
+
+
+ true
+
+
+ false
+
+
+ -1
+
+
+ 0.010000000000000
+
+
+ 0.025000000000000
+
+
+ -1.000000000000000
+
+
+ false
+
+
+ 0.300000000000000
+
+
+ 50.000000000000000
+
+
+ P
+
+
+
+
+
+ 0
+
+
+ %1.3f in
+
+
+ %1.2f mm
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ softKey
+V0
+
+
+ softkey-v0
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ SoftKey
+V1
+
+
+ softkey-v1
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ SoftKey
+V2
+
+
+ softkey-v2
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ SoftKey
+V3
+
+
+ softkey-v3
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ SoftKey
+V4
+
+
+ softkey-v4
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ SoftKey
+V5
+
+
+ softkey-v5
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ SoftKey
+V6
+
+
+ softkey-v6
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Reload
+
+
+ reload
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ SYSTEM
+Exit
+
+
+ exit
+
+
+ PushButton::BIT
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+ Axis Jog
+
+
+
-
+
+
+ +
+
+
+ jog-pos
+
+
+
+ -
+
+
+ -
+
+
+ jog-neg
+
+
+
+
+
+
+ -
+
+
+ linear rate / angular rate
+
+
+
-
+
+
+ Qt::Horizontal
+
+
+ jog-rate
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ jog-rate-angular
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ MPG
+
+
+
-
+
+
+ .001
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ mpg-scale-small
+
+
+ true
+
+
+ PushButton::FLOAT
+
+
+ mpg-scale
+
+
+ 0.001000000000000
+
+
+ buttonGroup_mpgscale
+
+
+
+ -
+
+
+ .01
+
+
+ true
+
+
+ true
+
+
+ mpg-scale-med
+
+
+ true
+
+
+ PushButton::FLOAT
+
+
+ mpg-scale
+
+
+ 0.010000000000000
+
+
+ buttonGroup_mpgscale
+
+
+
+ -
+
+
+ .1
+
+
+ true
+
+
+ true
+
+
+ mpg-scale-large
+
+
+ true
+
+
+ PushButton::FLOAT
+
+
+ mpg-scale
+
+
+ 0.100000000000000
+
+
+ buttonGroup_mpgscale
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 74
+
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ mpg-wheel
+
+
+
+
+
+ -
+
+
+ MDI Comands
+
+
+
-
+
+
+ 0
+
+
+ mdi-0
+
+
+
+ -
+
+
+ 1
+
+
+ mdi-1
+
+
+
+ -
+
+
+ 2
+
+
+ mdi-2
+
+
+
+
+
+
+ -
+
+
+ Mode Comands
+
+
+
-
+
+
+ Manual
+
+
+ false
+
+
+ true
+
+
+ manual-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ MDI
+
+
+ false
+
+
+ true
+
+
+ mdi-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Auto
+
+
+ false
+
+
+ true
+
+
+ auto-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ program control
+
+
+
-
+
+
+ start
+
+
+ cycle-start
+
+
+
+ -
+
+
+ pause
+
+
+ cycle-pause
+
+
+
+ -
+
+
+ Abort
+
+
+ cycle-abort
+
+
+
+
+
+
+ -
+
+
+ dialog control
+
+
+
-
+
+
+ ok
+
+
+ ok
+
+
+
+ -
+
+
+ cancel
+
+
+ cancel
+
+
+
+
+
+
+
+
+ -
+
+
+ Axis Selection
+
+
+
+ 0
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 4
+
+
-
+
+
+ None
+
+
+ true
+
+
+ true
+
+
+ axis-none
+
+
+
+ -
+
+
+ A
+
+
+ true
+
+
+ true
+
+
+ axis-a
+
+
+
+ -
+
+
+ Y
+
+
+ true
+
+
+ true
+
+
+ axis-y
+
+
+
+ -
+
+
+ X
+
+
+ true
+
+
+ true
+
+
+ axis-x
+
+
+
+ -
+
+
+ Z
+
+
+ true
+
+
+ true
+
+
+ axis-z
+
+
+
+ -
+
+
+ B
+
+
+ true
+
+
+ true
+
+
+ axis-b
+
+
+
+ -
+
+
+ C
+
+
+ true
+
+
+ true
+
+
+ axis-c
+
+
+
+ -
+
+
+ GUI
+
+
+ true
+
+
+ true
+
+
+ select-gui0
+
+
+
+
+
+
+ -
+
+
-
+
+
+ H0
+
+
+ softkey-h0
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H1
+
+
+ softkey-h1
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H2
+
+
+ softkey-h2
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H3
+
+
+ softkey-h3
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H4
+
+
+ softkey-h4
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H5
+
+
+ softkey-h5
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H6
+
+
+ softkey-h6
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H7
+
+
+ softkey-h7
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H8
+
+
+ softkey-h8
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ H9
+
+
+ softkey-h9
+
+
+ PushButton::BIT
+
+
+
+
+
+
+
+
+
+
+ IndicatedPushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ PushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ Dial
+ QDial
+ qtvcp.widgets.simple_widgets
+
+
+ ActionButton
+ IndicatedPushButton
+ qtvcp.widgets.action_button
+
+
+ StatusSlider
+ QSlider
+ qtvcp.widgets.status_slider
+
+
+
+
+
+
+
+
diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal
new file mode 100644
index 00000000000..22f87b4cea2
--- /dev/null
+++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal
@@ -0,0 +1,54 @@
+net rate halui.axis.jog-speed panel.jog-rate
+
+net sx halui.axis.x.select panel.axis-x
+net sx axis.x.jog-enable
+net sy halui.axis.y.select panel.axis-y
+net sy axis.y.jog-enable
+net sz halui.axis.z.select panel.axis-z
+net sz axis.z.jog-enable
+net sgui halui.mpg-select.0 panel.select-gui0
+
+
+net mpg-scale axis.x.jog-scale panel.mpg-scale
+net mpg-scale axis.y.jog-scale
+net mpg-scale axis.z.jog-scale
+
+net mpg-count panel.mpg-wheel-s
+net mpg-count qtdragon.mpg-in
+net mpg-count axis.x.jog-counts
+net mpg-count axis.y.jog-counts
+net mpg-count axis.z.jog-counts
+
+net jog-p halui.axis.selected.plus panel.jog-pos
+net jog-m halui.axis.selected.minus panel.jog-neg
+
+net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0
+net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1
+net m2 halui.gui.mdi-command-MACRO6 panel.mdi-2
+
+net man panel.manual-mode halui.mode.manual
+net mdi panel.mdi-mode halui.mode.mdi
+net auto panel.auto-mode halui.mode.auto
+
+net pause halui.cycle.start panel.cycle-start
+net start halui.cycle.pause panel.cycle-pause
+net abort halui.abort panel.cycle-abort
+
+net cancel halui.gui.cancel panel.cancel
+net ok halui.gui.ok panel.ok
+
+net softkey0 halui.gui.softkey-0 panel.softkey-0
+net softkey1 halui.gui.softkey-1 panel.softkey-1
+net softkey2 halui.gui.softkey-2 panel.softkey-2
+net softkey3 halui.gui.softkey-3 panel.softkey-3
+net softkey4 halui.gui.softkey-4 panel.softkey-4
+net softkey5 halui.gui.softkey-5 panel.softkey-5
+net softkey6 halui.gui.softkey-6 panel.softkey-6
+net softkey7 halui.gui.softkey-7 panel.softkey-7
+net softkey8 halui.gui.softkey-8 panel.softkey-8
+net softkey9 halui.gui.softkey-9 panel.softkey-9
+net softkey10 halui.gui.softkey-10 panel.softkey-10
+net softkey11 halui.gui.softkey-11 panel.softkey-11
+
+net exit halui.gui.shutdown panel.exit
+net reload halui.gui.reload-display panel.reload
diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.ui b/configs/sim/qtdragon/qtdragon_xyz/panel.ui
new file mode 100644
index 00000000000..ecef3135492
--- /dev/null
+++ b/configs/sim/qtdragon/qtdragon_xyz/panel.ui
@@ -0,0 +1,1327 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 583
+ 694
+
+
+
+ MainWindow
+
+
+
+ -
+
+
-
+
+
-
+
+
+ Axis Jog
+
+
+
-
+
+
+ +
+
+
+ jog-pos
+
+
+
+ -
+
+
+ -
+
+
+ jog-neg
+
+
+
+
+
+
+ -
+
+
+ linear rate / angular rate
+
+
+
-
+
+
+ Qt::Horizontal
+
+
+ jog-rate
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ jog-rate-angular
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ MPG
+
+
+
-
+
+
+ .001
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ mpg-scale-small
+
+
+ true
+
+
+ PushButton::FLOAT
+
+
+ mpg-scale
+
+
+ 0.001000000000000
+
+
+ buttonGroup_mpgscale
+
+
+
+ -
+
+
+ .01
+
+
+ true
+
+
+ true
+
+
+ mpg-scale-med
+
+
+ true
+
+
+ PushButton::FLOAT
+
+
+ mpg-scale
+
+
+ 0.010000000000000
+
+
+ buttonGroup_mpgscale
+
+
+
+ -
+
+
+ .1
+
+
+ true
+
+
+ true
+
+
+ mpg-scale-large
+
+
+ true
+
+
+ PushButton::FLOAT
+
+
+ mpg-scale
+
+
+ 0.100000000000000
+
+
+ buttonGroup_mpgscale
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 74
+
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ mpg-wheel
+
+
+
+
+
+ -
+
+
+ MDI Comands
+
+
+
-
+
+
+ 0
+
+
+ mdi-0
+
+
+
+ -
+
+
+ 1
+
+
+ mdi-1
+
+
+
+ -
+
+
+ 2
+
+
+ mdi-2
+
+
+
+
+
+
+ -
+
+
+ Mode Comands
+
+
+
-
+
+
+ Manual
+
+
+ false
+
+
+ true
+
+
+ manual-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ MDI
+
+
+ false
+
+
+ true
+
+
+ mdi-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ Auto
+
+
+ false
+
+
+ true
+
+
+ auto-mode
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ program control
+
+
+
-
+
+
+ start
+
+
+ cycle-start
+
+
+
+ -
+
+
+ pause
+
+
+ cycle-pause
+
+
+
+ -
+
+
+ Abort
+
+
+ cycle-abort
+
+
+
+
+
+
+ -
+
+
+ dialog control
+
+
+
-
+
+
+ ok
+
+
+ ok
+
+
+
+ -
+
+
+ cancel
+
+
+ cancel
+
+
+
+
+
+
+
+
+ -
+
+
+ Axis Selection
+
+
+
+ 0
+
+
+ 3
+
+
+ 0
+
+
+ 0
+
+
+ 4
+
+
-
+
+
+ None
+
+
+ true
+
+
+ true
+
+
+ axis-none
+
+
+
+ -
+
+
+ A
+
+
+ true
+
+
+ true
+
+
+ axis-a
+
+
+
+ -
+
+
+ Y
+
+
+ true
+
+
+ true
+
+
+ axis-y
+
+
+
+ -
+
+
+ X
+
+
+ true
+
+
+ true
+
+
+ axis-x
+
+
+
+ -
+
+
+ Z
+
+
+ true
+
+
+ true
+
+
+ axis-z
+
+
+
+ -
+
+
+ B
+
+
+ true
+
+
+ true
+
+
+ axis-b
+
+
+
+ -
+
+
+ C
+
+
+ true
+
+
+ true
+
+
+ axis-c
+
+
+
+ -
+
+
+ GUI
+
+
+ true
+
+
+ true
+
+
+ select-gui0
+
+
+
+
+
+
+ -
+
+
-
+
+
+ ESTOP
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ 255
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ 0.300000000000000
+
+
+ 10
+
+
+ 0
+
+
+ 0
+
+
+ 5.000000000000000
+
+
+ 0.300000000000000
+
+
+ 0.900000000000000
+
+
+ True
+
+
+ False
+
+
+ print("true command")
+
+
+ print("false command")
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ 0
+
+
+ true
+
+
+ false
+
+
+ -1
+
+
+ 0.010000000000000
+
+
+ 0.025000000000000
+
+
+ -1.000000000000000
+
+
+ false
+
+
+ 0.300000000000000
+
+
+ 50.000000000000000
+
+
+ P
+
+
+
+
+
+ 0
+
+
+ %1.3f in
+
+
+ %1.2f mm
+
+
+
+ -
+
+
+ ON
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ 255
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ 0.300000000000000
+
+
+ 10
+
+
+ 0
+
+
+ 0
+
+
+ 5.000000000000000
+
+
+ 0.300000000000000
+
+
+ 0.900000000000000
+
+
+ True
+
+
+ False
+
+
+ print("true command")
+
+
+ print("false command")
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ 0
+
+
+ true
+
+
+ false
+
+
+ -1
+
+
+ 0.010000000000000
+
+
+ 0.025000000000000
+
+
+ -1.000000000000000
+
+
+ false
+
+
+ 0.300000000000000
+
+
+ 50.000000000000000
+
+
+ P
+
+
+
+
+
+ 0
+
+
+ %1.3f in
+
+
+ %1.2f mm
+
+
+
+ -
+
+
+ HOME
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
+ 255
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ 0
+ 0
+ 0
+
+
+
+ 0.300000000000000
+
+
+ 10
+
+
+ 0
+
+
+ 0
+
+
+ 5.000000000000000
+
+
+ 0.300000000000000
+
+
+ 0.900000000000000
+
+
+ True
+
+
+ False
+
+
+ print("true command")
+
+
+ print("false command")
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ 0
+
+
+ true
+
+
+ false
+
+
+ -1
+
+
+ 0.010000000000000
+
+
+ 0.025000000000000
+
+
+ -1.000000000000000
+
+
+ false
+
+
+ 0.300000000000000
+
+
+ 50.000000000000000
+
+
+ P
+
+
+
+
+
+ 0
+
+
+ %1.3f in
+
+
+ %1.2f mm
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ MainTab
+
+
+ softkey-0
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ FileTab
+
+
+ softkey-1
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Offsets
+
+
+ softkey-2
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Tools
+
+
+ softkey-3
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Status
+
+
+ softkey-4
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Probe
+
+
+ softkey-5
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Gcodes
+
+
+ softkey-6
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Setup
+
+
+ softkey-7
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Settings
+
+
+ softkey-8
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Utils
+
+
+ softkey-9
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ User
+
+
+ softkey-10
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Cam
+
+
+ softkey-11
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Reload
+
+
+ reload
+
+
+ PushButton::BIT
+
+
+
+ -
+
+
+ exit
+
+
+ exit
+
+
+ PushButton::BIT
+
+
+
+
+
+
+
+
+
+
+ IndicatedPushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ PushButton
+ QPushButton
+ qtvcp.widgets.simple_widgets
+
+
+ Dial
+ QDial
+ qtvcp.widgets.simple_widgets
+
+
+ ActionButton
+ IndicatedPushButton
+ qtvcp.widgets.action_button
+
+
+ StatusSlider
+ QSlider
+ qtvcp.widgets.status_slider
+
+
+
+
+
+
+
+
diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini
new file mode 100644
index 00000000000..5efd17e4785
--- /dev/null
+++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini
@@ -0,0 +1,274 @@
+# This file was created with the 7i96 Wizard on Jun 10 2019 11:12:47
+# Changes to most things are ok and will be read by the wizard
+
+[EMC]
+VERSION = 1.1
+MACHINE = qtdragon_metric
+DEBUG = 0x00000000
+
+[DISPLAY]
+# sets qtdragon as screen. for debug output to terminal add -d or -v
+# sets window title
+# sets icon in task manager
+DISPLAY = qtvcp -d qtdragon
+TITLE = QtDragon XYZ Metric
+ICON = silver_dragon.png
+
+# qtdragon saves most preference to this file
+PREFERENCE_FILE_PATH = WORKINGFOLDER/qtdragon.pref
+
+# min/max percentage overrides allowed in qtdragon 1 = 100%
+MAX_FEED_OVERRIDE = 1.2
+MIN_SPINDLE_0_OVERRIDE = 0.5
+MAX_SPINDLE_0_OVERRIDE = 1.2
+
+# manual spindle speed will start at this RPM
+DEFAULT_SPINDLE_0_SPEED = 12000
+
+# spindle up/down increment in RPM
+SPINDLE_INCREMENT = 200
+
+# min max apindle speed manually allowed
+MIN_SPINDLE_0_SPEED = 1000
+MAX_SPINDLE_0_SPEED = 20000
+
+# max spindle power in Watts
+MAX_SPINDLE_POWER = 2000
+
+# min/max/default jog velocities in qtdragon in units/sec
+MIN_LINEAR_VELOCITY = 0
+MAX_LINEAR_VELOCITY = 60.00
+DEFAULT_LINEAR_VELOCITY = 50.0
+
+# incremental jog step length options
+INCREMENTS = 10 mm, 1.0 mm, 0.10 mm, 0.01 mm, 1.0 inch, 0.1 inch, 0.01 inch
+
+# Display grid increments
+GRIDS = 0, .1 mm, 1 mm, 2 mm, 5 mm, 10 mm, .25 in, .5 in
+
+CYCLE_TIME = 100
+INTRO_GRAPHIC = silver_dragon.png
+INTRO_TIME = 2
+
+# default program search path
+PROGRAM_PREFIX = ~/linuxcnc/nc_files
+
+# NGCGUI subroutine path.
+# Thr path must also be in [RS274NGC] SUBROUTINE_PATH
+NGCGUI_SUBFILE_PATH = ../../../nc_files/ngcgui_lib/
+# pre selected programs tabs
+# specify filenames only, files must be in the NGCGUI_SUBFILE_PATH
+NGCGUI_SUBFILE = slot.ngc
+NGCGUI_SUBFILE = qpocket.ngc
+
+# qtdragon saves MDI cxommands to this file
+MDI_HISTORY_FILE = mdi_history.dat
+# qtdragon saves rnning logs to this file
+LOG_FILE = qtdragon.log
+
+# optional user dialogs (3), controlled by HAL pins
+MESSAGE_BOLDTEXT = Critical and Persistent
+MESSAGE_TEXT = This is a persistent dialog test
+MESSAGE_DETAILS = There seems to be something wrong\n You must fix it to clear message
+MESSAGE_TYPE = nonedialog
+MESSAGE_PINNAME = nonedialogtest
+MESSAGE_ICON = CRITICAL
+
+MESSAGE_BOLDTEXT = Do You Want To Make A Choice?
+MESSAGE_TEXT = This is a yes no dialog test
+MESSAGE_DETAILS = Y/N DETAILS
+MESSAGE_TYPE = yesnodialog
+MESSAGE_PINNAME = yndialogtest
+MESSAGE_ICON = QUESTION
+
+MESSAGE_BOLDTEXT = This is an information message
+MESSAGE_TEXT = This is low priority
+MESSAGE_DETAILS = press ok to clear
+MESSAGE_TYPE = okdialog status
+MESSAGE_PINNAME = bothtest
+MESSAGE_ICON = INFO
+
+# optional tab showing an external qtvcp panel
+EMBED_TAB_NAME=Vismach demo
+EMBED_TAB_COMMAND=qtvcp vismach_mill_xyz
+EMBED_TAB_LOCATION=tabWidget_utilities
+
+[MDI_COMMAND_LIST]
+# for macro buttons on main page up to 10 possible
+MDI_COMMAND_MACRO0 = G53 G0 Z0;G0 X0 Y0;Z0, Goto\nUser\nZero
+MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
+
+[MACROS]
+MACRO_COMMAND_MACRO6 = go_to_position x-pos y-pos z-pos, Go To \nPosition
+MACRO_COMMAND_MACRO2 = lost, LOST
+MACRO_COMMAND_MACRO3 = increment x-incr y-incr, INCR
+MACRO_COMMAND_MACRO4 = macro_4
+MACRO_COMMAND_MACRO5 = macro_5
+
+[FILTER]
+# Controls what programs are shown inqtdragon file manager
+PROGRAM_EXTENSION = .ngc,.nc,.tap G-Code File (*.ngc,*.nc,*.tap)
+PROGRAM_EXTENSION = .png,.gif,.jpg Greyscale Depth Image
+PROGRAM_EXTENSION = .py Python Script
+
+# specifies what special 'filter' programs runs based on program ending
+png = image-to-gcode
+gif = image-to-gcode
+jpg = image-to-gcode
+py = python3
+
+[KINS]
+KINEMATICS = trivkins coordinates=XYZ
+JOINTS = 3
+
+[EMCIO]
+TOOL_TABLE = tool.tbl
+
+[RS274NGC]
+# motion controller saves parameters to this file
+PARAMETER_FILE = qtdragon.var
+
+# start up G/M codes when first loaded
+RS274NGC_STARTUP_CODE = G17 G21 G40 G43H0 G54 G64P0.0127 G80 G90 G94 G97 M5 M9
+
+# subroutine/remap path list
+SUBROUTINE_PATH = ../../../../nc_files/probe/basic_probe/macros:~/linuxcnc/nc_files/examples/ngcgui_lib:~/linuxcnc/nc_files/examples/ngcgui_lib/utilitysubs:./macros
+
+# on abort, this ngc file is called. required for basic/versa probe
+ON_ABORT_COMMAND=O call
+
+[EMCMOT]
+EMCMOT = motmod
+SERVO_PERIOD = 1000000
+COMM_TIMEOUT = 1.0
+COMM_WAIT = 0.010
+BASE_PERIOD = 100000
+
+[TASK]
+TASK = milltask
+CYCLE_TIME = 0.010
+
+[TRAJ]
+COORDINATES = XYZ
+LINEAR_UNITS = metric
+ANGULAR_UNITS = degree
+MAX_LINEAR_VELOCITY = 60.00
+DEFAULT_LINEAR_VELOCITY = 50.00
+SPINDLES = 1
+
+[HAL]
+HALUI = halui
+#HALBRIDGE = hal_bridge
+
+# loads the HAL machine simulation
+HALFILE = core_sim.hal
+HALFILE = simulated_home.hal
+
+# this file is loaded after qtdragon has made it's HAl pins
+# you can add multiple entries
+POSTGUI_HALFILE = qtdragon_postgui.hal
+
+# this command is run after qtdragon has made it's HAl pins
+# any HAL conmmand can be used
+# you can add multiple entries
+# uncomment this one to print all HAL pins that start with qt
+#POSTGUI_HALCMD = show pin qt
+POSTGUI_HALCMD = show pin halui.gui
+POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel
+[HALUI]
+# no content
+
+[PROBE]
+# pick basic probe or versa probe or remove for none
+#USE_PROBE = versaprobe
+USE_PROBE = basicprobe
+
+[AXIS_X]
+MIN_LIMIT = -0.001
+MAX_LIMIT = 520.0
+MAX_VELOCITY = 60.0
+MAX_ACCELERATION = 500.0
+
+[AXIS_Y]
+MIN_LIMIT = -0.001
+MAX_LIMIT = 630.0
+MAX_VELOCITY = 60.0
+MAX_ACCELERATION = 500.0
+
+[AXIS_Z]
+# used by external offsets for auto spindle lift
+OFFSET_AV_RATIO = 0.2
+MIN_LIMIT = -115.0
+MAX_LIMIT = 10.0
+MAX_VELOCITY = 40.0
+MAX_ACCELERATION = 500.0
+
+[JOINT_0]
+AXIS = X
+MIN_LIMIT = -0.001
+MAX_LIMIT = 520.0
+MAX_VELOCITY = 60.0
+MAX_ACCELERATION = 500.0
+TYPE = LINEAR
+SCALE = 160.0
+STEPGEN_MAX_VEL = 72.0
+STEPGEN_MAX_ACC = 600.0
+FERROR = 1.0
+MIN_FERROR = 0.5
+MAX_OUTPUT = 0
+MAX_ERROR = 0.0127
+HOME = 20.0
+HOME_OFFSET = 0.00000
+HOME_SEARCH_VEL = 20.000000
+HOME_LATCH_VEL = 10.000
+HOME_SEQUENCE = 1
+HOME_USE_INDEX = False
+HOME_IGNORE_LIMITS = False
+HOME_IS_SHARED = 1
+
+[JOINT_1]
+AXIS = Y
+MIN_LIMIT = -0.001
+MAX_LIMIT = 630.0
+MAX_VELOCITY = 60.0
+MAX_ACCELERATION = 500.0
+TYPE = LINEAR
+SCALE = 160.0
+STEPGEN_MAX_VEL = 72.0
+STEPGEN_MAX_ACC = 600.0
+FERROR = 1.0
+MIN_FERROR = 0.5
+MAX_OUTPUT = 0
+MAX_ERROR = 0.0127
+HOME = 20.0
+HOME_OFFSET = 0.000000
+HOME_SEARCH_VEL = 20.00
+HOME_LATCH_VEL = 10.00
+HOME_SEQUENCE = 2
+HOME_USE_INDEX = False
+HOME_IGNORE_LIMITS = False
+
+[JOINT_2]
+AXIS = Z
+MIN_LIMIT = -115.0
+MAX_LIMIT = 10.0
+MAX_VELOCITY = 40.0
+MAX_ACCELERATION = 500.0
+TYPE = LINEAR
+SCALE = 160.0
+STEPGEN_MAX_VEL = 48.0
+STEPGEN_MAX_ACC = 600.0
+FERROR = 1.0
+MIN_FERROR = 0.5
+MAX_OUTPUT = 0
+MAX_ERROR = 0.0127
+HOME = -10.0
+HOME_OFFSET = 0.000000
+HOME_SEARCH_VEL = 20.000000
+HOME_LATCH_VEL = 10.00
+HOME_SEQUENCE = 0
+HOME_USE_INDEX = False
+HOME_IGNORE_LIMITS = False
+HOME_IS_SHARED = 1
+
+
diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini
index 7e5d56e05aa..f437be0811a 100644
--- a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini
+++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini
@@ -151,7 +151,7 @@ SPINDLES = 1
[HAL]
HALUI = halui
-HALBRIDGE = hal_bridge
+#HALBRIDGE = hal_bridge
# loads the HAL machine simulation
HALFILE = core_sim.hal
diff --git a/debian/configure b/debian/configure
index 096b3f4a5ed..635bf664ecb 100755
--- a/debian/configure
+++ b/debian/configure
@@ -121,7 +121,7 @@ PYTHON_GST=python3-gst-1.0,gstreamer1.0-plugins-base
TCLTK_VERSION=8.6
PYTHON_IMAGING=python3-pil
PYTHON_IMAGING_TK=python3-pil.imagetk
-QTVCP_DEPENDS="python3-qtpy,\n python3-pyqt5,\n python3-pyqt5.qsci,\n python3-pyqt5.qtsvg,\n python3-pyqt5.qtopengl,\n python3-opencv,\n python3-dbus,\n python3-espeak,\n python3-dbus.mainloop.pyqt5,\n python3-pyqt5.qtwebengine,\n espeak-ng,\n pyqt5-dev-tools,\n gstreamer1.0-tools,\n espeak,\n sound-theme-freedesktop"
+QTVCP_DEPENDS="python3-qtpy,\n python3-pyqt5,\n python3-pyqt5.qsci,\n python3-pyqt5.qtsvg,\n python3-pyqt5.qtopengl,\n python3-opencv,\n python3-dbus,\n python3-espeak,\n python3-dbus.mainloop.pyqt5,\n python3-pyqt5.qtwebengine,\n espeak-ng,\n pyqt5-dev-tools,\n gstreamer1.0-tools,\n espeak,\n sound-theme-freedesktop,\n python3-zmq"
YAPPS_RUNTIME="python3-yapps"
DEBHELPER="debhelper (>= 12)"
COMPAT="12"
diff --git a/debian/control.main-pkg.in b/debian/control.main-pkg.in
index 56653dc0827..2af4d385f91 100644
--- a/debian/control.main-pkg.in
+++ b/debian/control.main-pkg.in
@@ -22,6 +22,7 @@ Depends:
python3-opengl,
python3-configobj,
python3-xlib,
+ python3-zmq,
libgtksourceview-4-dev,
tcl@TCLTK_VERSION@,
tk@TCLTK_VERSION@,
diff --git a/lib/python/bridgeui/__init__.py b/lib/python/bridgeui/__init__.py
new file mode 100644
index 00000000000..b28b04f6431
--- /dev/null
+++ b/lib/python/bridgeui/__init__.py
@@ -0,0 +1,3 @@
+
+
+
diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py
new file mode 100644
index 00000000000..e04ccb7837d
--- /dev/null
+++ b/lib/python/bridgeui/bridge.py
@@ -0,0 +1,296 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import time
+
+import json
+import signal
+
+import hal
+from common.iniinfo import _IStat as IStatParent
+from common import logger
+
+# LOG is for running code logging
+LOG = logger.initBaseLogger('HAL bridge', log_file=None,
+ log_level=logger.WARNING, logToFile=False)
+
+# Force the log level for this module
+LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
+
+try:
+ import zmq
+ ZMQ = True
+except:
+ LOG.critical('ZMQ python library problem - Is python3-zmq installed?')
+ ZMQ = False
+
+class Info(IStatParent):
+ _instance = None
+ _instanceNum = 0
+
+ def __new__(cls, *args, **kwargs):
+ if not cls._instance:
+ cls._instance = IStatParent.__new__(cls, *args, **kwargs)
+ return cls._instance
+
+# Instantiate the library with global reference
+
+
+class Bridge(object):
+ def __init__(self, readAddress = "tcp://127.0.0.1:5690",
+ writeAddress = "tcp://127.0.0.1:5691"):
+ super(Bridge, self).__init__()
+ self.INFO = Info()
+
+ self.currentSelectedAxis = 'None'
+ self.axesSelected = {'X':0,'Y':0,'Z':0,'A':0,'B':0,'C':0,
+ 'U':0,'V':0,'W':0,'MPG0':0}
+ self.readAddress = readAddress
+ self.writeAddress = writeAddress
+ LOG.debug('read port: {}'.format(readAddress))
+ LOG.debug('write port: {}'.format(writeAddress))
+
+ self.readTopic = ""
+ self.writeTopic = "STATUSREQUEST"
+
+ # catch control c and terminate signals
+ signal.signal(signal.SIGTERM, self.shutdown)
+ signal.signal(signal.SIGINT, self.shutdown)
+
+ self.init()
+ if ZMQ:
+ self.init_read()
+ self.init_write()
+
+ def init(self):
+ self.jogRate = 0
+ self.jogRateAngular = 0
+
+ self.jogIncrement = 0
+ self.jogIncrementAngular = 0
+
+ self.activeJoint = 0
+
+ def init_write(self):
+ context = zmq.Context()
+ self.writeSocket = context.socket(zmq.PUB)
+ self.writeSocket.bind(self.writeAddress)
+
+ def init_read(self):
+ # ZeroMQ Context
+ self.readContext = zmq.Context()
+
+ # Define the socket using the "Context"
+ self.readSocket = self.readContext.socket(zmq.SUB)
+
+ # Define subscription and messages with topic to accept.
+ self.readSocket.setsockopt_string(zmq.SUBSCRIBE, self.readTopic)
+ self.readSocket.connect(self.readAddress)
+
+ # callback from ZMQ read socket
+ def readMsg(self):
+ if self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN:
+ while self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN:
+ # get raw message
+ topic, data = self.readSocket.recv_multipart()
+ # convert from json object to python object
+ y = json.loads(data)
+ self. action(y.get('MESSAGE'),y.get('ARGS'))
+
+ # set our variables from messages from hal_glib
+ def action(self, msg, data):
+ LOG.debug('{} -> {} -> {}'.format(msg, data, data[0]))
+ if msg == 'jograte-changed':
+ self.jogRate = float(data[0])
+ elif msg == 'jograte-angular-changed':
+ self.jogRateAngular = float(data[0])
+ elif msg == 'jogincrements-changed':
+ self.jogIncrement = float(data[0][0])
+ elif msg == 'jogincrement-angular-changed':
+ self.jogIncremtAngular = float(data[0][0])
+ elif msg == 'joint-selection-changed':
+ self.activeJoint = int(data[0])
+ elif msg == 'axis-selection-changed':
+ print ('pre axis state', self.axesSelected,self.currentSelectedAxis)
+ flag = 1
+ if data[0] == 'MPG0':
+ self.currentSelectedAxis = data[0]
+ flag = 0
+ self.axesSelected['MPG0'] = True
+ else:
+ self.axesSelected['MPG0'] = False
+ for i in(self.INFO.AVAILABLE_AXES):
+ if data[0] == i:
+ state = True
+ self.currentSelectedAxis = data[0]
+ flag = 0
+ else:
+ state = False
+ self.axesSelected[i] = int(state)
+
+ if flag:
+ self.currentSelectedAxis = 'None'
+ print ('axis state', self.axesSelected,self.currentSelectedAxis)
+
+ # send msg to hal_glib
+ def writeMsg(self, msg, data=''):
+ #print('Write Msg called')
+ if ZMQ:
+ topic = self.writeTopic
+ message = json.dumps({'FUNCTION':msg,'ARGS':data})
+ LOG.debug('Sending ZMQ Message:{} {}'.format(topic, message))
+ self.writeSocket.send_multipart(
+ [bytes(topic.encode('utf-8')),
+ bytes((message).encode('utf-8'))])
+
+ def shutdown(self,signum=None,stack_frame=None):
+ LOG.debug('shutdown')
+ global app
+ app.quit()
+
+ def cycleStart(self):
+ # cycle start
+ self.writeMsg('request_cycle_start', True)
+
+ def cyclePause(self):
+ self.writeMsg('request_cycle_pause', True)
+
+ def ok(self):
+ self.writeMsg('request_ok', True)
+
+ def cancel(self):
+ self.writeMsg('request_cancel', True)
+
+ def reloadDisplay(self):
+ self.writeMsg('request_reload_display', True)
+
+ def shutdownController(self):
+ self.writeMsg('request_shutdown')
+
+ def softkey(self, index):
+ print(f'Softkey index {index}')
+ self.writeMsg('request_softkey', index)
+
+ # if the number is bigger then MDI command list
+ # then look for MACRO commands
+ def getMdiName(self, num):
+ if num >len(self.INFO.MDI_COMMAND_DICT)-1:
+ offset = len(self.INFO.MDI_COMMAND_DICT)
+ return self.getMacroName(num-offset)
+ else:
+ temp = list(self.INFO.MDI_COMMAND_DICT.keys())[num]
+ LOG.debug('MDI:{} {}'.format(num,temp))
+ return temp
+
+ def getMacroName(self, num):
+ if num >len(self.INFO.MACRO_COMMAND_DICT)-1:
+ return 'None'
+ temp = list(self.INFO.MACRO_COMMAND_DICT.keys())[num]
+ LOG.debug('MACRO:{} {}'.format(num,temp))
+ return temp
+
+ def runIndexedMacro(self, num):
+ # check for any MDI commands first:
+ name = self.getMdiName(num)
+ LOG.debug('Macro name:{} ,index: {}'.format(name, num))
+ if name != 'None':
+ self.writeMsg('request_macro_call', name)
+
+ # else look for any MACRO commands:
+ else:
+ offset = len(self.INFO.MDI_COMMAND_DICT)
+ name = self.getMacroName(num-offset)
+ LOG.debug('Macro name:{} ,index: {}'.format(name, num))
+ if name != 'None':
+ self.writeMsg('request_macro_call', name)
+
+ # cound of MDI and MACRO commands
+ def getMdiCount(self):
+ #print('->',len(self.INFO.MDI_COMMAND_DICT),len(self.INFO.MACRO_COMMAND_DICT))
+ return len(self.INFO.MDI_COMMAND_DICT) + len(self.INFO.MACRO_COMMAND_DICT)
+
+ def getJogRate(self):
+ return self.jogRate
+ def setJogRate(self, value):
+ self.writeMsg('set_jograte', value)
+
+ def getJogRateAngular(self):
+ return self.jogRateAngular
+ def setJogRateAngular(self, value):
+ self.writeMsg('set_jograte_angular', value)
+
+ # XYZABCUVW, None, or MPG0
+ def getSelectedAxis(self):
+ name = self.currentSelectedAxis
+ if name == 'None':
+ index = -1
+ elif name =='MPG0':
+ index = 100
+ else:
+ index = 'XYZABCUVW'.index(name)
+ return index
+ def setSelectedAxis(self, value):
+ if value < 0:
+ letter = 'None'
+ elif value == 100:
+ letter = 'MPG0'
+ else:
+ letter ='XYZABCUVW'[value]
+ self.writeMsg('set_selected_axis', letter)
+
+ def isAxisSelected(self, index):
+ if index == 100:
+ letter = 'MPG0'
+ else:
+ letter = 'XYZABCUVW'[index]
+ return int(self.axesSelected[letter])
+
+ def getAxisIndexType(self, axis):
+ try:
+ num = self.INFO.GET_JOINT_NUM_FROM_AXIS_INDEX[axis]
+ flag = self.INFO.JOINT_TYPE_INT[num]
+ return flag
+ except:
+ return 1
+
+
+ def __getitem__(self, item):
+ return getattr(self, item)
+ def __setitem__(self, item, value):
+ return setattr(self, item, value)
+
+if __name__ == "__main__":
+ import sys
+ import getopt
+ from PyQt5.QtWidgets import QApplication
+
+ letters = 'dh' # the : means an argument needs to be passed after the letter
+ keywords = ['readport=', 'writeport=' ] # the = means that a value is expected after
+ # the keyword
+
+ opts, extraparam = getopt.getopt(sys.argv[1:],letters,keywords)
+ # starts at the second element of argv since the first one is the script name
+ # extraparms are extra arguments passed after all option/keywords are assigned
+ # opts is a list containing the pair "option"/"value"
+
+ readport = "tcp://127.0.0.1:5690"
+ writeport = "tcp://127.0.0.1:5691"
+
+ for o,p in opts:
+ if o in ['-d']:
+ LOG.setLevel(logger.DEBUG)
+ elif o in ['--readport']:
+ readport = p
+ elif o in ['--writeport']:
+ writeport = p
+ elif o in ['-h','--help']:
+ print('HAL bridge: GUI to HAL interface using ZMQ')
+ print('option "-d" = debug print mode')
+ print('option "--readport=" read socket address')
+ print('option "--writeport=" write socket address')
+ print('example: hal_bridge -d --readport=tcp://127.0.0.1:5692')
+
+ app = QApplication(sys.argv)
+ test = Bridge(readport, writeport)
+ sys.exit(app.exec_())
diff --git a/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py
index 864bf95b0ec..7da10816f76 100644
--- a/lib/python/common/hal_glib.py
+++ b/lib/python/common/hal_glib.py
@@ -248,6 +248,7 @@ class _GStat(GObject.GObject):
'cycle-start-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)),
'cycle-pause-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)),
'macro-call-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)),
+ 'softkey-pressed': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE, (GObject.TYPE_INT,)),
}
STATES = { linuxcnc.STATE_ESTOP: 'state-estop'
@@ -353,6 +354,11 @@ def __init__(self, stat = None):
def set_timer(self):
GLib.timeout_add(CYCLE_TIME, self.update)
+ # used to run the Gobject mainloop once
+ # allows a GUI that is not GLib based to update the mainloop
+ def run_iteration(self):
+ GLib.MainContext.default().iteration (True)
+
# open a zmq socket for writing out data
def init_write_socket(self):
context = zmq.Context()
@@ -363,6 +369,7 @@ def init_write_socket(self):
self.write_available = True
except Exception as e:
LOG.debug('hal_glib write socket not available: {}'.format(e))
+ LOG.debug('hal_glib write socket not available\n {}'.format(e))
self.write_available = False
# convert and actually send out the message
@@ -398,30 +405,41 @@ def init_read_socket(self):
GObject.IO_IN|GObject.IO_ERR|GObject.IO_HUP,
self.onReadMsg, self.readSocket)
- # convert message to a function name and data
- # then call that function
+ # called directly to process any current message
+ def readNextMsg(self):
+ event = self.readSocket.poll(timeout=0)
+ if event & zmq.POLLIN:
+ self.convertMsg()
+
+ # called when GObject notices a change
def onReadMsg(self, queue, condition, sock):
while self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN:
- # get raw message
- topic, data = self.readSocket.recv_multipart()
- # convert from json object to python object
- y = json.loads(data)
- function = y.get('FUNCTION')
- data = y.get('ARGS')
- LOG.debug('REQUESTED:{}'.format(y))
- if data == '':
- try:
- self[function]()
- except Exception as e:
- LOG.debug('not a valid request\n {}'.format(e))
- else:
- try:
- self[function](data)
- except Exception as e:
- LOG.debug('not a valid request\n {}'.format(e))
- #self. action(y.get('MESSAGE'),y.get('ARGS'))
+ self.convertMsg()
return True
+ # convert message to a function name and data
+ # then call that function
+ def convertMsg(self):
+ # get raw message
+ topic, data = self.readSocket.recv_multipart()
+ LOG.debug(f'RAW REQUESTED:{topic},{data}')
+ # convert from json object to python object
+ y = json.loads(data)
+ function = y.get('FUNCTION')
+ data = y.get('ARGS')
+ LOG.debug('REQUESTED:{}'.format(y))
+ if data == '':
+ try:
+ self[function]()
+ except Exception as e:
+ LOG.debug('not a valid request\n {}'.format(e))
+ else:
+ try:
+ self[function](data)
+ except Exception as e:
+ LOG.debug('not a valid request\n {}'.format(e))
+ #self. action(y.get('MESSAGE'),y.get('ARGS'))
+
def merge(self):
self.old['command-state'] = self.stat.state
self.old['state'] = self.stat.task_state
@@ -1481,6 +1499,9 @@ def request_ok(self, data):
def request_cancel(self, data):
self.emit('cancel-request', data)
+ def request_softkey(self, index):
+ self.emit('softkey-pressed', index)
+
#############################################
def shutdown(self):
diff --git a/lib/python/common/iniinfo.py b/lib/python/common/iniinfo.py
index 57b78707bd7..c81118f6e37 100644
--- a/lib/python/common/iniinfo.py
+++ b/lib/python/common/iniinfo.py
@@ -18,7 +18,7 @@ def __init__(self, ini=None):
global LOG
LOG = logger.getLogger(__name__)
# Force the log level for this module only
- #LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
+ LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
inipath = os.environ.get('INI_FILE_NAME', '/dev/null')
self.LINUXCNC_IS_RUNNING = bool(inipath != '/dev/null')
@@ -570,7 +570,7 @@ def update(self):
if self.INI.hassection('MDI_COMMAND_LIST'):
try:
for key,value in self.INI.getvariables('MDI_COMMAND_LIST'):
-
+ #print(f'key:{key},value:{value}')
# legacy way: list of repeat 'MDI_COMMAND=XXXX'
# in this case order matters in the INI
if key == 'MDI_COMMAND':
@@ -597,24 +597,51 @@ def update(self):
# new way: 'MDI_COMMAND_SSS = XXXX' (SSS being any string)
# order of commands doesn't matter in the INI
+
+ # here are some samples, the last three are difficult
+ # the third is invalid
+ # MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero
+ # cmd: G53 G0 Z0;G53 G0 X0 Y0 label: Goto\nMachn\nZero
+
+ # MDI_COMMAND_MACRO2 = (MSG, macro 2);
+ # cmd: (MSG, macro 2); label:
+
+ # MDI_COMMAND_MACRO3 = (MSG, macro 2)
+ # cmd: (MSG label: macro 2)
+
+ # MDI_COMMAND_MACRO4 = (MSG, macro 2),test
+ # cmd: (MSG, macro 2) label: test
+
else:
self.MDI_COMMAND_LIST.append(None)
self.MDI_COMMAND_LABEL_LIST.append(None)
try:
name = (key.replace('MDI_COMMAND_',''))
mdidatadict = {}
- for num,k in enumerate(value.split(',')):
- if num == 0:
- mdidatadict['cmd'] = k
- if len(value.split(',')) <2:
- mdidatadict['label'] = None
- else:
- mdidatadict['label'] = k
+ #print(f'name:{name}')
+ # find the last colon in string or 0
+ lastCmd = value.rfind(';')
+ #print('l ;:',lastCmd)
+ if lastCmd == -1: lastCmd = 0
+
+ # find the last colon in string or use the string length
+ lastComma = value.rfind(',', lastCmd)
+ #print('l comma:',lastComma,lastCmd)
+ if lastComma == -1: lastComma = len(value)
+
+ label = value[lastComma+1:]
+ cmd = value[:lastComma]
+ #print(value,' cmd:',cmd,' label:',label)
+
+ mdidatadict['cmd'] = cmd
+ mdidatadict['label'] = label
self.MDI_COMMAND_DICT[name] = mdidatadict
+
except Exception as e:
LOG.error('INI MDI command parse error:{}'.format(e))
except Exception as e:
LOG.error('INI MDI command parse error:{}'.format(e))
+ print(self.MDI_COMMAND_DICT)
################
# MACRO commands #
diff --git a/lib/python/gladevcp/gtk_action.py b/lib/python/gladevcp/gtk_action.py
index 247d9df4e93..834b62ac665 100644
--- a/lib/python/gladevcp/gtk_action.py
+++ b/lib/python/gladevcp/gtk_action.py
@@ -191,20 +191,39 @@ def CALL_MDI_WAIT(self, code, time=5, mode_return=False):
self.ensure_mode(premode)
return 0
- def CALL_INI_MDI(self, number):
+ def CALL_INI_MDI(self, key, mode_return = False):
try:
- mdi = INFO.MDI_COMMAND_LIST[number]
+ # prefer named INI MDI commands
+ mdi = INFO.get_ini_mdi_command(key)
+ LOG.debug('COMMAND= {}'.format(mdi))
+ if mdi is None: raise Exception
except:
- msg = 'MDI_COMMAND= # {} Not found under [MDI_COMMAND_LIST] in INI file'.format(number)
- LOG.error(msg)
- self.SET_ERROR_MESSAGE(msg)
- return
+ # fallback to legacy nth line
+ try:
+ mdi = INFO.MDI_COMMAND_LIST[key]
+ except:
+ msg = 'MDI_COMMAND_{} Not found under [MDI_COMMAND_LIST] in INI file'.format(key)
+ LOG.error(msg)
+ self.SET_ERROR_MESSAGE(msg)
+ return
+
+
mdi_list = mdi.split(';')
+ if mode_return:
+ self.RECORD_CURRENT_MODE()
+ self._a = STATUS.connect('command-stopped', lambda w: self.return_mode_after_finish())
self.ensure_mode(linuxcnc.MODE_MDI)
for code in (mdi_list):
LOG.debug('CALL_INI_MDI command:{}'.format(code))
self.cmd.mdi('%s' % code)
+ # when command stops - we try to continue the generator.
+ # if generator is done - return to recorded mode.
+ def return_mode_after_finish(self):
+ print('ini command end')
+ self.RESTORE_RECORDED_MODE()
+ STATUS.handler_disconnect(self._a)
+
def CALL_OWORD(self, code, time=5):
LOG.debug('OWORD_COMMAND= {}'.format(code))
self.ensure_mode(linuxcnc.MODE_MDI)
@@ -597,21 +616,29 @@ def ADJUST_GRAPHICS_PAN(self, x, y):
def ADJUST_GRAPHICS_ROTATE(self, x, y):
STATUS.emit('graphics-view-changed', 'rotate-view', {'X': x, 'Y': y})
+ #TODO without the gtk dialog, gnome-sessions
+ # does not start reliably
def SHUT_SYSTEM_DOWN_PROMPT(self):
- import subprocess
- try:
- try:
- subprocess.call('gnome-session-quit --power-off', shell=True)
- except:
- try:
- subprocess.call('xfce4-session-logout', shell=True)
- except:
- try:
- subprocess.call('systemctl poweroff', shell=True)
- except:
- raise
- except Exception as e:
- LOG.warning("Couldn't shut system down: {}".format(e))
+ import shutil
+ import time
+
+ dialog = gtk.MessageDialog(parent=None,
+ message_type=gtk.MessageType.QUESTION,
+ buttons=gtk.ButtonsType.YES_NO,
+ text='Shutdown System?')
+ dialog.set_keep_above(True)
+ dialog.format_secondary_text('Unsaved data will be lost')
+ response = dialog.run()
+ dialog.destroy()
+ if response == gtk.ResponseType.YES:
+
+ if shutil.which('gnome-session-quit'):
+ subprocess.run(["gnome-session-quit", "--power-off"])
+ elif shutil.which('xfce4-session-logout'):
+ subprocess.call('xfce4-session-logout', shell=True)
+ else:
+ # force a shutdown - no prompt
+ subprocess.call('systemctl poweroff', shell=True)
def SHUT_SYSTEM_DOWN_NOW(self):
import subprocess
diff --git a/lib/python/gladevcp/hal_gremlin.py b/lib/python/gladevcp/hal_gremlin.py
index c0cfd5675ea..d7badb66747 100644
--- a/lib/python/gladevcp/hal_gremlin.py
+++ b/lib/python/gladevcp/hal_gremlin.py
@@ -139,6 +139,7 @@ def __init__(self, *a, **kw):
def reloadfile(self,w):
try:
self.fileloaded(None,self._reload_filename)
+ self.clear_live_plotter()
except:
pass
self.gstat.emit('graphics-gcode-properties',self.gcode_properties)
diff --git a/lib/python/gladevcp/speedcontrol.py b/lib/python/gladevcp/speedcontrol.py
index 332f025ab0d..36cde39a08e 100755
--- a/lib/python/gladevcp/speedcontrol.py
+++ b/lib/python/gladevcp/speedcontrol.py
@@ -35,6 +35,8 @@
else:
from .hal_widgets import _HalSpeedControlBase
+from gladevcp.core import Status, Action
+
class SpeedControl(Gtk.Box, _HalSpeedControlBase):
'''
The SpeedControl Widget serves as a slider with button to increment od decrease
@@ -98,6 +100,8 @@ class SpeedControl(Gtk.Box, _HalSpeedControlBase):
"%.1f", GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT),
'do_hide_button' : ( GObject.TYPE_BOOLEAN, 'Hide the button', 'Display the button + and - to alter the values',
False, GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT),
+ 'type' : ( GObject.TYPE_INT, 'Type of adjustment', 'Set to -1 for general, 0 for jograte, 1 for angular jograte',
+ -1, 1, -1, GObject.ParamFlags.READWRITE|GObject.ParamFlags.CONSTRUCT),
}
__gproperties = __gproperties__
@@ -112,6 +116,9 @@ class SpeedControl(Gtk.Box, _HalSpeedControlBase):
def __init__(self, size = 36, value = 0, min = 0, max = 100, inc_speed = 100, unit = "", color = "#FF8116", template = "%.1f"):
super(SpeedControl, self).__init__()
+ self._action = Action()
+ self._status = Status()
+
# basic settings
self._size = size
self._value = value
@@ -123,6 +130,8 @@ def __init__(self, size = 36, value = 0, min = 0, max = 100, inc_speed = 100, un
self._increment = (self._max - self._min) / 100.0
self._template = template
self._speed = inc_speed
+ self.type_linear_jog = False
+ self.type_angular_jog = False
self.adjustment = Gtk.Adjustment(value = self._value, lower = self._min, upper = self._max, step_increment = self._increment, page_increment = 0)
self.adjustment.connect("value_changed", self._on_value_changed)
@@ -175,6 +184,13 @@ def _hal_init(self):
self.hal_pin_decrease = self.hal.newpin(self.hal_name+".decrease", hal.HAL_BIT, hal.HAL_IN)
self.hal_pin_decrease.connect("value-changed", self._on_minus_changed)
+ if self.type_linear_jog:
+ print('linear jograte ->>',self.type_linear_jog)
+ self._status.connect('jograte-changed', lambda w, data: self.set_value(data))
+ elif self.type_angular_jog:
+ print('Angular jograte ->>',self.type_angular_jog)
+ self._status.connect('jograte-angular-changed', lambda w, data: self.set_value(data))
+
# this draws our widget on the screen
def expose(self, widget, event):
# create the cairo window
@@ -269,6 +285,12 @@ def get_value(self):
# we are not sync, so
def _on_value_changed(self, widget):
value = widget.get_value()
+
+ if self.type_linear_jog:
+ self._action.SET_JOG_RATE(value)
+ elif self.type_angular_jog:
+ self._action.SET_JOG_RATE_ANGULAR(value)
+
if value != self._value:
self._value = value
self.set_value(self._value)
@@ -453,6 +475,14 @@ def do_set_property(self, property, value):
self._template = value
if name == "do_hide_button":
self.hide_button(value)
+ if name == "type":
+ print(name,value)
+ if value == 0:
+ self.type_linear_jog = True
+ self.type_angular_jog = False
+ elif value == 1:
+ self.type_linear_jog = False
+ self.type_angular_jog = True
self._draw_widget()
else:
raise AttributeError('unknown property %s' % property.name)
diff --git a/lib/python/qtvcp/qt_action.py b/lib/python/qtvcp/qt_action.py
index afe02a62b89..a30f852bbe8 100644
--- a/lib/python/qtvcp/qt_action.py
+++ b/lib/python/qtvcp/qt_action.py
@@ -228,7 +228,7 @@ def CALL_MDI_WAIT(self, code, time=5, mode_return=False):
self.ensure_mode(premode)
return 0
- def CALL_INI_MDI(self, key):
+ def CALL_INI_MDI(self, key, mode_return = False):
try:
# prefer named INI MDI commands
mdi = INFO.get_ini_mdi_command(key)
@@ -245,6 +245,9 @@ def CALL_INI_MDI(self, key):
return
mdi_list = mdi.split(';')
+ if mode_return:
+ self.RECORD_CURRENT_MODE()
+ self._a = STATUS.connect('command-stopped', lambda w: self.return_mode_after_finish())
self.ensure_mode(linuxcnc.MODE_MDI)
for code in (mdi_list):
LOG.debug('CALL_INI_MDI command:{}'.format(code))
@@ -283,6 +286,13 @@ def RUN_MACRO( self, data):
self.CALL_MDI(command)
return
+ # when command stops - we try to continue the generator.
+ # if generator is done - return to recorded mode.
+ def return_mode_after_finish(self):
+ print('ini command end')
+ self.RESTORE_RECORDED_MODE()
+ STATUS.handler_disconnect(self._a)
+
def CALL_OWORD(self, code, time=5):
LOG.debug('OWORD_COMMAND= {}'.format(code))
self.ensure_mode(linuxcnc.MODE_MDI)
diff --git a/lib/python/qtvcp/widgets/axis_tool_button.py b/lib/python/qtvcp/widgets/axis_tool_button.py
index d2c58f7a55b..9080ae29efd 100644
--- a/lib/python/qtvcp/widgets/axis_tool_button.py
+++ b/lib/python/qtvcp/widgets/axis_tool_button.py
@@ -134,6 +134,8 @@ def homed_on_test():
self.hal_pin_joint = self.HAL_GCOMP_.newpin(str(pname + '-joint'), hal.HAL_BIT, hal.HAL_OUT)
self.hal_pin_axis = self.HAL_GCOMP_.newpin(str(pname + '-axis'), hal.HAL_BIT, hal.HAL_OUT)
STATUS.connect('general',self.return_value)
+ if 'MPG' in self._axis.upper():
+ self.settingMenu.clear()
def _enableGroup(self, state,bstate):
for i in(self.zeroButton, self.setButton,self.divideButton,
@@ -226,6 +228,7 @@ def _a_from_j(self, axis):
return axis, r[jnum]
def selectJoint(self):
+ print(self.objectName(),f'select: j{self._joint} a{self._axis} ck{self.isChecked()}')
if self._block_signal or self._joint == -1 or self._axis == '': return
if self.isChecked() == True:
if STATUS.is_joint_mode():
@@ -251,7 +254,8 @@ def selectJoint(self):
self.hal_pin_axis.set(False)
def ChangeState(self, joint = None, axis = None):
- #print(self.objectName(),'change',joint,axis,self._axis)
+ #print(self.objectName(),f'change: j{joint} a{axis} type{self._axis} ck{self.isChecked()}')
+
# joint mode
if STATUS.is_joint_mode():
if int(joint) != self._joint:
@@ -269,12 +273,18 @@ def ChangeState(self, joint = None, axis = None):
# axis mode
else:
if str(axis) != self._axis and self.isChecked():
+ #print(self.objectName(),'Set false')
+ if not self.group() in (0,None):
+ self.group().setExclusive(False)
self._block_signal = True
self.setChecked(False)
self._block_signal = False
+ if not self.group() in (0,None):
+ self.group().setExclusive(True)
if self._halpin_option and self._axis != '':
self.hal_pin_joint.set(False)
elif str(axis) == self._axis and not self.isChecked():
+ #print(self.objectName(),'Set True')
self._block_signal = True
self.setChecked(True)
self._block_signal = False
@@ -311,6 +321,8 @@ def set_axis(self, data):
self.goToG53Button.setText(text)
text = 'Go To G5x Origin in {}'.format(self._axis)
self.goToG5xButton.setText(text)
+ elif data.upper() in('MPG0','MPG1'):
+ self._axis = str(data.upper())
else:
self._axis = str('')
diff --git a/lib/python/qtvcp/widgets/dialog_widget.py b/lib/python/qtvcp/widgets/dialog_widget.py
index e0f9735afb9..a0fa1028c1b 100644
--- a/lib/python/qtvcp/widgets/dialog_widget.py
+++ b/lib/python/qtvcp/widgets/dialog_widget.py
@@ -416,6 +416,7 @@ def __init__(self, parent=None):
class ToolDialog(LcncDialog, GeometryMixin):
def __init__(self, parent=None):
super(ToolDialog, self).__init__(parent)
+ self._request_name = 'TOOLCHANGE'
self.setText('Manual Tool Change Request')
self.setInformativeText('Please Insert Tool 0')
self.setStandardButtons(QMessageBox.Ok)
@@ -458,6 +459,8 @@ def _hal_init(self):
self.sound_type = self.PREFS_.getpref('toolDialog_sound_type', 'READY', str, 'DIALOG_OPTIONS')
else:
self.play_sound = False
+ # can acknowledge from status messages too
+ STATUS.connect('dialog-update', self._status_update)
# process callback from 'change' HAL pin
def tool_change(self, change):
@@ -508,7 +511,7 @@ def tool_change(self, change):
# process callback for 'change-button' HAL pin
# hide the message dialog or desktop notify message
def external_acknowledge(self, state):
- #print('external acklnowledge: {}'.format(state))
+ #print('external acknowledge: {}'.format(state))
if state:
if self._useDesktopNotify:
self.deskNotice.close()
@@ -516,10 +519,29 @@ def external_acknowledge(self, state):
self.hide()
self._processChange(True)
+ # callback from status 'update-dialog'
+ def _status_update(self, w, message):
+ print(message)
+ if message.get('NAME') == self._request_name:
+ if not self.isVisible(): return
+ print(self._request_name)
+ response = message.get('response')
+ if not response is None:
+ # 'ok'
+ if response == 1:
+ if self._useDesktopNotify:
+ self.deskNotice.close()
+ elif self.isVisible():
+ self.hide()
+ self._processChange(True)
+ # 'cancel'
+ elif response == 0:
+ self.hide()
+ self._processChange(False)
# This also is called from DesktopDialog
def _processChange(self,answer):
- #print('proces change: {}'.format(answer))
+ print('process change: {}'.format(answer))
if answer == -1:
self.changed.set(True)
ACTION.ABORT()
@@ -535,6 +557,16 @@ def _processChange(self,answer):
self.record_geometry()
STATUS.emit('focus-overlay-changed', False, None, None)
+ # decode button presses
+ def msgbtn(self, i):
+ LOG.debug('Button pressed is: {}'.format(i.text()))
+ if self.clickedButton() == self._actionbutton:
+ self._processChange(-1)
+ elif self.standardButton(self.clickedButton()) == QMessageBox.Ok:
+ self._processChange(True)
+ else:
+ self._processChange(False)
+
###### overridden functions ################
def showdialog(self, message, more_info=None, details=None,
@@ -576,16 +608,6 @@ def showEvent(self, event):
self.set_geometry()
super(LcncDialog, self).showEvent(event)
- # decode button presses
- def msgbtn(self, i):
- LOG.debug('Button pressed is: {}'.format(i.text()))
- if self.clickedButton() == self._actionbutton:
- self._processChange(-1)
- elif self.standardButton(self.clickedButton()) == QMessageBox.Ok:
- self._processChange(True)
- else:
- self._processChange(False)
-
############################################
# **********************
@@ -1849,6 +1871,10 @@ def showdialog(self, preload=None, overlay=True, cycle=False, wait=False):
if wait:
self._flag = True
while self._flag:
+ # read any ZMQ messages
+ # then update widgets
+ # till we get a dialog answer
+ STATUS.readNextMsg()
QApplication.processEvents()
return (self.display.text(), self._result)
else:
diff --git a/lib/python/qtvcp/widgets/widget_baseclass.py b/lib/python/qtvcp/widgets/widget_baseclass.py
index 23bfa396f45..d2012fe8f82 100644
--- a/lib/python/qtvcp/widgets/widget_baseclass.py
+++ b/lib/python/qtvcp/widgets/widget_baseclass.py
@@ -17,6 +17,7 @@
# the other subclasses are for simple HAL widget functionality
import hal
+
from qtpy.QtCore import Property
from qtpy.QtWidgets import QDialog
diff --git a/share/qtvcp/screens/qtdragon/qtdragon.ui b/share/qtvcp/screens/qtdragon/qtdragon.ui
index d853a2198f4..4edf09dff06 100644
--- a/share/qtvcp/screens/qtdragon/qtdragon.ui
+++ b/share/qtvcp/screens/qtdragon/qtdragon.ui
@@ -3504,7 +3504,7 @@ PLATE
QFrame::Raised
- 0
+ 9
@@ -5800,9 +5800,9 @@ LOG
0
- 0
- 192
- 547
+ -351
+ 306
+ 734
@@ -12677,69 +12677,6 @@ LIFT
- -
-
-
-
- 1
- 0
-
-
-
-
- 58
- 36
-
-
-
-
- 16777215
- 76
-
-
-
- MPG
-SCROLL
-
-
- true
-
-
-
-
-
- true
-
-
- false
-
-
-
- 255
- 0
- 255
-
-
-
- 4
-
-
- 0.300000000000000
-
-
- 0
-
-
- 0
-
-
- false
-
-
- selectButtonGroup
-
-
-
-
@@ -14573,6 +14510,81 @@ ALL
+ -
+
+
+
+ 1
+ 0
+
+
+
+
+ 58
+ 36
+
+
+
+
+ 16777215
+ 76
+
+
+
+ MPG
+SCROLL
+
+
+ true
+
+
+ false
+
+
+ QToolButton::DelayedPopup
+
+
+ true
+
+
+
+ 255
+ 0
+ 255
+
+
+
+ 4
+
+
+ 0
+
+
+ 0
+
+
+ -2
+
+
+ MPG0
+
+
+ CALCULATOR
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+ false
+
+
+
@@ -18094,7 +18106,7 @@ ALL
2
- 02:37:25
+ 08:01:11 PM
Qt::AlignCenter
@@ -19231,4 +19243,3 @@ ALL
-
diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py
index 8280b2b8291..f0efe8888eb 100644
--- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py
+++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py
@@ -153,10 +153,12 @@ def __init__(self, halcomp, widgets, paths):
STATUS.connect('status-message', lambda w, d, o: self.add_external_status(d,o))
STATUS.connect('runstop-line-changed', lambda w, l :self.lastRunLine(l))
STATUS.connect('cycle-start-request', lambda w, state :self.btn_start_clicked(state))
- STATUS.connect('cycle-pause-request', lambda w, state: self.btn_pause_clicked(state))
+ STATUS.connect('cycle-pause-request', lambda w, state: self.ext_pause_toggled(state))
STATUS.connect('macro-call-request', lambda w, name: self.request_macro_call(name))
STATUS.connect('ok-request', lambda w, state: self.dialog_ext_control(w,1,1))
STATUS.connect('cancel-request', lambda w, state: self.dialog_ext_control(w,1,0))
+ STATUS.connect('axis-selection-changed', lambda w,data: self.mpg_selection_changed(data))
+ STATUS.connect('softkey-pressed', lambda w,data: self.softkey_pressed(data))
self.swoopPath = os.path.join(paths.IMAGEDIR,'lcnc_swoop.png')
self.swoopURL = QtCore.QUrl.fromLocalFile(self.swoopPath)
@@ -783,7 +785,7 @@ def dialog_return(self, w, message):
self.touchoff('touchplate')
elif sensor_code and name == 'MESSAGE' and rtn is True:
self.touchoff('sensor')
- elif wait_code and name == 'MESSAGE':
+ elif wait_code and name == 'MESSAGE' and rtn is True:
self.lowerSpindle()
elif unhome_code and name == 'MESSAGE' and rtn is True:
ACTION.SET_MACHINE_UNHOMED(-1)
@@ -916,20 +918,24 @@ def lastRunLine(self, line):
# called from hal_glib to run macros from external event
def request_macro_call(self, data):
+ #print(f'macro call data: {data}')
if not self.w.chk_auto_mode_ext_macro.isChecked() and not STATUS.is_mdi_mode():
self.add_status(_translate("HandlerClass",'Machine must be in MDI mode to run macros'), WARNING)
return
-
- if 'ini-macro-cmd' in data:
+ cmd = INFO.get_ini_mdi_command(data)
+ #print(f'MDI command:{cmd} data:{data}')
+ if INFO.get_ini_mdi_command(data) is None:
data = data.replace('ini-macro-cmd-','')
try:
temp = INFO.MACRO_COMMAND_DICT.get(data).get('cmd')
+ #print(temp)
self.run_macro(data=temp)
return
- except:
- self.add_status(_translate(f"HandlerClass",'External requested INI macro data not recognized:{data}'), CRITICAL)
+ except Exception as e:
+ print(e)
+ self.add_status(_translate("HandlerClass",f'External requested INI macro data not recognized:{data}'), CRITICAL)
- elif 'ini-mdi-cmd' in data:
+ elif INFO.get_ini_macro_command(data) is None:
for b in range(0,10):
button = self.w['macrobutton{}'.format(b)]
# prefer named INI MDI commands
@@ -952,9 +958,20 @@ def request_macro_call(self, data):
self.add_status(_translate("HandlerClass",'Error running macro: {} {}\n{}'.format(key, text, e)))
break
else:
- self.add_status(_translate(f"HandlerClass",'External requested INI mdi {data} does not match button name/number'), CRITICAL)
+ self.add_status(_translate("HandlerClass",f'External requested INI mdi {data} does not match button name/number'), CRITICAL)
else:
- self.add_status(_translate(f"HandlerClass",'External requested INI macro data not recognized:{data}'), CRITICAL)
+ self.add_status(_translate("HandlerClass",f'External requested INI macro data not recognized:{data}'), CRITICAL)
+
+ # external request for a softkey press from HALUI/halbridge
+ def softkey_pressed(self, index):
+ tmp=['main','file','offsets','tool','status',
+ 'probe','gcodes','setup','settings',
+ 'utils','user','camera']
+
+ btn = self.w[f'btn_{tmp[index]}']
+ #print(f'index{index}, btn, {btn}')
+ if btn.isVisible():
+ btn.click()
#######################
# CALLBACKS FROM FORM #
@@ -989,10 +1006,20 @@ def cmb_gcode_history_clicked(self):
# program frame
def btn_start_clicked(self, obj):
+
if not STATUS.is_all_homed():
self.add_status(_translate("HandlerClass","Machine must be is homed"), CRITICAL)
return
- if not os.path.exists(self.last_loaded_program):
+ if STATUS.is_auto_paused():
+ self.command.auto(linuxcnc.AUTO_RESUME)
+ return
+ if STATUS.is_mdi_mode():
+ self.w.mdiline.submit()
+ return
+ if STATUS.is_man_mode():
+ self.add_status(_translate("HandlerClass","Can't start cycles or submit MDI commands in manual Mode"),WARNING)
+ return
+ if not os.path.exists(self.last_loaded_program):
self.add_status(_translate("HandlerClass","No program to execute"), WARNING)
return
if not STATUS.is_auto_mode():
@@ -1314,6 +1341,12 @@ def btn_spindle_z_down_clicked(self):
if self.h['eoffset-clear'] != True:
self.h['eoffset-spindle-count'] = int(fval)
+ def ext_pause_toggled(self, state):
+ if STATUS.is_auto_paused():
+ self.btn_pause_clicked(False)
+ return
+ self.btn_pause_clicked(True)
+
def btn_pause_clicked(self, data):
# pause request
@@ -1447,6 +1480,12 @@ def btn_systemtool_toggled(self, state):
if state:
STATUS.emit('dro-reference-change-request', 1)
+ def mpg_selection_changed(self, data):
+ if data =='MPG0':
+ self.recolorMPGFocusBorder()
+ elif data == 'None':
+ self.removeMPGFocusBorder()
+
def MPG_select_changed(self, button):
#print(button)
# Auto exclusive doesn't allow unchecking all buttons
@@ -1463,8 +1502,10 @@ def MPG_select_changed(self, button):
if button == self.w.btn_mpg_scroll:
if self.w.btn_mpg_scroll.isChecked():
self.recolorMPGFocusBorder()
- else:
+ else:
self.removeMPGFocusBorder()
+ else:
+ self.removeMPGFocusBorder()
#self.set_statusbar('MPG output Selected: {}'.format(cmd.toolTip()),DEFAULT,noLog=True)
self._lastSelectButton = button
diff --git a/src/Makefile b/src/Makefile
index c95d6d3473c..b0f46de708e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -756,6 +756,7 @@ install-kernel-indep: install-dirs
install-kernel-indep: install-python
install-python: install-dirs
$(DIR) $(DESTDIR)$(SITEPY) $(DESTDIR)$(SITEPY)/rs274
+ $(DIR) $(DESTDIR)$(SITEPY)/bridgeui
$(DIR) $(DESTDIR)$(SITEPY)/common
$(DIR) $(DESTDIR)$(SITEPY)/touchy
$(DIR) $(DESTDIR)$(SITEPY)/gscreen
@@ -779,6 +780,7 @@ install-python: install-dirs
$(DIR) $(DESTDIR)$(SITEPY)/pyui
$(DIR) $(DESTDIR)$(SITEPY)/plasmac
$(FILE) ../lib/python/*.py ../lib/python/*.so $(DESTDIR)$(SITEPY)
+ $(FILE) ../lib/python/bridgeui/*.py $(DESTDIR)$(SITEPY)/bridgeui
$(FILE) ../lib/python/common/*.py $(DESTDIR)$(SITEPY)/common
$(FILE) ../lib/python/rs274/*.py $(DESTDIR)$(SITEPY)/rs274
$(FILE) ../lib/python/touchy/*.py $(DESTDIR)$(SITEPY)/touchy
diff --git a/src/emc/usr_intf/Submakefile b/src/emc/usr_intf/Submakefile
index b914e30ffe1..4dbd9ec9afb 100644
--- a/src/emc/usr_intf/Submakefile
+++ b/src/emc/usr_intf/Submakefile
@@ -42,5 +42,6 @@ TARGETS += ../bin/linuxcnclcd
../bin/halui: $(call TOOBJS, $(HALUISRCS)) ../lib/liblinuxcnc.a ../lib/liblinuxcncini.so.1 ../lib/libnml.so.0 ../lib/liblinuxcnchal.so.0 ../lib/libtooldata.so.0
$(ECHO) Linking $(notdir $@)
- $(Q)$(CXX) $(CXXFLAGS) -o $@ $(ULFLAGS) $^ $(LDFLAGS)
+ $(Q)$(CXX) $(CXXFLAGS) -o $@ $(ULFLAGS) $^ $(LDFLAGS) $(PYTHON_LIBS) $(PYTHON_EXTRA_LIBS)
TARGETS += ../bin/halui
+
diff --git a/src/emc/usr_intf/gmoccapy/dialogs.py b/src/emc/usr_intf/gmoccapy/dialogs.py
index 08a16ee7249..dc9418f37ad 100644
--- a/src/emc/usr_intf/gmoccapy/dialogs.py
+++ b/src/emc/usr_intf/gmoccapy/dialogs.py
@@ -38,8 +38,25 @@ class Dialogs(GObject.GObject):
'play_sound': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)),
}
- def __init__(self):
+ def __init__(self, caller):
GObject.GObject.__init__(self)
+ self.sys_dialog = self.system_dialog(caller)
+ self.warn_dialog = self.warning_dialog(caller)
+ self.ent_dialog = self.entry_dialog(caller)
+ self.yn_dialog = self.yesno_dialog(caller)
+
+ # sent from Gstat messages
+ # first one found visibly gets the answer
+ # maybe we should check focus?
+ def dialog_ext_control(self, answer):
+ if self.sys_dialog.get_visible():
+ self.sys_dialog.response(answer)
+ elif self.warn_dialog.get_visible():
+ self.warn_dialog.response(answer)
+ elif self.ent_dialog.get_visible():
+ self.ent_dialog.response(answer)
+ elif self.yn_dialog.get_visible():
+ self.yn_dialog.response(answer)
# This dialog is for unlocking the system tab
# The unlock code number is defined at the top of the page
@@ -47,9 +64,12 @@ def system_dialog(self, caller):
dialog = Gtk.Dialog(_("Enter System Unlock Code"),
caller.widgets.window1,
Gtk.DialogFlags.DESTROY_WITH_PARENT)
+ dialog.set_modal(True)
label = Gtk.Label(_("Enter System Unlock Code"))
label.modify_font(Pango.FontDescription("sans 20"))
calc = gladevcp.Calculator()
+ dialog._calc = calc
+ dialog._caller = caller
dialog.vbox.pack_start(label, False, False, 0)
dialog.vbox.add(calc)
calc.set_value("")
@@ -57,59 +77,101 @@ def system_dialog(self, caller):
calc.set_editable(True)
calc.integer_entry_only(True)
calc.num_pad_only(True)
- calc.entry.connect("activate", lambda w : dialog.emit("response", Gtk.ResponseType.ACCEPT))
+ calc.entry.connect("activate", lambda w : self.on_system_response(dialog,Gtk.ResponseType.ACCEPT))
dialog.parse_geometry("360x400")
dialog.set_decorated(True)
+ dialog.connect("response", self.on_system_response)
+ return dialog
+
+ def show_system_dialog(self):
+ dialog = self.sys_dialog
+ dialog._calc.set_value("")
dialog.show_all()
self.emit("play_sound", "alert")
- response = dialog.run()
- code = calc.get_value()
- dialog.destroy()
- if response == Gtk.ResponseType.ACCEPT:
- if code == int(caller.unlock_code):
- return True
- return False
- def entry_dialog(self, caller, data = None, header = _("Enter value") , label = _("Enter the value to set"), integer = False):
- dialog = Gtk.Dialog(header,
+ # wait but don't block event loop
+ dialog.RESPONSE = None
+ while dialog.RESPONSE is None:
+ while Gtk.events_pending():
+ Gtk.main_iteration()
+
+ dialog.hide()
+
+ code = dialog._calc.get_value()
+ rtn = -1
+ if dialog.RESPONSE == Gtk.ResponseType.ACCEPT:
+ if code == int(dialog._caller.unlock_code):
+ rtn = 1
+ else:
+ rtn = 0
+
+ return rtn
+
+ def on_system_response(self, dialog, rtn):
+ dialog.RESPONSE = rtn
+
+ def entry_dialog(self, caller):
+ dialog = Gtk.Dialog('',
caller.widgets.window1,
Gtk.DialogFlags.DESTROY_WITH_PARENT)
- label = Gtk.Label(label)
- label.modify_font(Pango.FontDescription("sans 20"))
- label.set_margin_top(15)
- calc = gladevcp.Calculator()
+ dialog.label = Gtk.Label('')
+ dialog.label.modify_font(Pango.FontDescription("sans 20"))
+ dialog.label.set_margin_top(15)
+ dialog.calc = gladevcp.Calculator()
content_area = dialog.get_content_area()
- content_area.pack_start(child=label, expand=False, fill=False, padding=0)
- content_area.add(calc)
- if data != None:
- calc.set_value(data)
- else:
- calc.set_value("")
- calc.set_property("font", "sans 20")
- calc.set_editable(True)
- calc.entry.connect("activate", lambda w : dialog.emit("response", Gtk.ResponseType.ACCEPT))
+ content_area.pack_start(child=dialog.label, expand=False, fill=False, padding=0)
+ content_area.add(dialog.calc)
+ dialog.calc.set_property("font", "sans 20")
+ dialog.calc.set_editable(True)
+ dialog.calc.entry.connect("activate", lambda w : self.on_entry_response(dialog, Gtk.ResponseType.ACCEPT))
dialog.parse_geometry("460x400")
dialog.set_decorated(True)
+ dialog.connect("response", self.on_entry_response)
+ return dialog
+
+ def show_entry_dialog(self, data = None, header = _("Enter value") ,
+ label = _("Enter the value to set"), integer = False):
+
+ dialog = self.ent_dialog
+ if data != None:
+ dialog.calc.set_value(data)
+ else:
+ dialog.calc.set_value("")
if integer: # The user is only allowed to enter integer values, we hide some button
- calc.integer_entry_only(True)
- calc.num_pad_only(True)
+ dialog.calc.integer_entry_only(True)
+ dialog.calc.num_pad_only(True)
+ dialog.label.set_text(label)
+ dialog.set_title(header)
dialog.show_all()
- response = dialog.run()
- value = calc.get_value()
- dialog.destroy()
- if response == Gtk.ResponseType.ACCEPT:
+
+ # wait but don't block event loop
+ dialog.RESPONSE = None
+ while dialog.RESPONSE is None:
+ while Gtk.events_pending():
+ Gtk.main_iteration()
+
+ dialog.hide()
+
+ value = dialog.calc.get_value()
+ if dialog.RESPONSE == Gtk.ResponseType.ACCEPT:
if value != None:
- if integer:
- return int(value)
+ if dialog.calc.integer_only:
+ qv = int(value)
else:
- return float(value)
+ qv = float(value)
else:
- return "ERROR"
- return "CANCEL"
+ qv = "ERROR"
+ else:
+ qv = "CANCEL"
+ return qv
+
+ def on_entry_response(self, dialog, rtn):
+ dialog.RESPONSE = rtn
# display warning dialog
- def warning_dialog(self, caller, message, secondary = None, title = _("Operator Message"),\
+ def warning_dialog(self, caller, message = '', secondary = None, title = _("Operator Message"),\
sound = True, confirm_pin = 'warning-confirm', active_pin = None):
+
dialog = Gtk.MessageDialog(caller.widgets.window1,
Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.INFO, Gtk.ButtonsType.NONE, message)
@@ -123,11 +185,10 @@ def warning_dialog(self, caller, message, secondary = None, title = _("Operator
box.add(ok_button)
dialog.action_area.add(box)
dialog.set_border_width(5)
- dialog.show_all()
if sound:
self.emit("play_sound", "alert")
dialog.set_title(title)
-
+ dialog.context = []
def periodic():
if caller.halcomp[confirm_pin]:
dialog.response(Gtk.ResponseType.OK)
@@ -138,19 +199,37 @@ def periodic():
return False
return True
GLib.timeout_add(100, periodic)
+ dialog.connect("response", self.on_warning_response)
+ return dialog
- response = dialog.run()
- dialog.destroy()
- return response == Gtk.ResponseType.OK
+ def show_warning_dialog(self, title, message, sound=True,
+ confirm_pin = 'warning-confirm', active_pin = None):
+ dialog = self.warn_dialog
+ dialog.set_title(title)
+ dialog.format_secondary_text(message)
+ dialog.set_markup(message)
+ dialog.show_all()
+ if sound:
+ self.emit("play_sound", "alert")
+
+ # wait but don't block event loop
+ dialog.RESPONSE = None
+ while dialog.RESPONSE is None:
+ while Gtk.events_pending():
+ Gtk.main_iteration()
+
+ dialog.hide()
+
+ return dialog.RESPONSE
- def yesno_dialog(self, caller, message, title = _("Operator Message")):
+ def on_warning_response(self, dialog, rtn):
+ dialog.RESPONSE = rtn
+
+ def yesno_dialog(self, caller):
dialog = Gtk.MessageDialog(caller.widgets.window1,
Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.QUESTION,
Gtk.ButtonsType.NONE)
- if title:
- dialog.set_title(str(title))
- dialog.set_markup(message)
yes_button = Gtk.Button.new_with_mnemonic(_("_Yes"))
no_button = Gtk.Button.new_with_mnemonic(_("_No"))
yes_button.set_size_request(-1, 56)
@@ -164,11 +243,30 @@ def yesno_dialog(self, caller, message, title = _("Operator Message")):
box.set_layout(Gtk.ButtonBoxStyle.CENTER)
dialog.action_area.add(box)
dialog.set_border_width(5)
+ dialog.connect("response", self.on_yn_response)
+ return dialog
+
+ def show_yesno_dialog(self, caller, message, title = _("Operator Message")):
+ dialog = self.yn_dialog
+ dialog.set_markup(message)
+ if title:
+ dialog.set_title(str(title))
dialog.show_all()
self.emit("play_sound", "alert")
- response = dialog.run()
- dialog.destroy()
- return response == Gtk.ResponseType.YES
+
+ # wait but don't block event loop
+ dialog.RESPONSE = None
+ while dialog.RESPONSE is None:
+ while Gtk.events_pending():
+ Gtk.main_iteration()
+
+ rtn = dialog.RESPONSE
+ dialog.hide()
+ return bool(rtn in(Gtk.ResponseType.YES, Gtk.ResponseType.ACCEPT))
+
+ # update internal variable so dialog will respond
+ def on_yn_response(self,dialog, rtn):
+ dialog.RESPONSE = rtn
def show_user_message(self, caller, message, title = _("Operator Message")):
dialog = Gtk.MessageDialog(caller.widgets.window1,
diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.glade b/src/emc/usr_intf/gmoccapy/gmoccapy.glade
index 049c965ed4f..622a1cc2c4c 100644
--- a/src/emc/usr_intf/gmoccapy/gmoccapy.glade
+++ b/src/emc/usr_intf/gmoccapy/gmoccapy.glade
@@ -1,5 +1,5 @@
-
+
@@ -1410,6 +1410,7 @@ uncomment selection
False
rgb(255,129,22)
10500
+ 0
mm/min
1500
@@ -1456,6 +1457,7 @@ uncomment selection
rgb(255,129,22)
3600
%.d
+ 1
°/min
360
diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py
index c7afde2fcbf..58ba3707705 100644
--- a/src/emc/usr_intf/gmoccapy/gmoccapy.py
+++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py
@@ -198,7 +198,8 @@ def __init__(self, argv):
self.error_channel.poll()
# set INI path for INI info class before widgets are loaded
- INFO = Info(ini=argv[2])
+ self.INFO = Info(ini=argv[2])
+ self.ACTION = Action()
self.builder = Gtk.Builder()
# translation of the glade file will be done with
@@ -227,6 +228,8 @@ def __init__(self, argv):
self.so_counts = 0 # need to calculate difference in counts to change the spindle override slider
self.jv_counts = 0 # need to calculate difference in counts to change the jog_vel slider
self.ro_counts = 0 # need to calculate difference in counts to change the rapid override slider
+ self._last_count = 0 # need to calculate MPG count difference
+ self.mpg_enabled = False
self.spindle_override = 1 # holds the feed override value and is needed to be able to react to halui pin
self.feed_override = 1 # holds the spindle override value and is needed to be able to react to halui pin
@@ -271,9 +274,6 @@ def __init__(self, argv):
self.icon_theme.append_search_path(ICON_THEME_DIR)
self.icon_theme.append_search_path(USER_ICON_THEME_DIR)
- self.dialogs = dialogs.Dialogs()
- self.dialogs.connect("play_sound", self._on_play_sound)
-
# check the arguments given from the command line (Ini file)
self.user_mode = False
self.logofile = None
@@ -352,6 +352,9 @@ def __init__(self, argv):
self.builder.connect_signals(self)
+ self.dialogs = dialogs.Dialogs(caller = self)
+ self.dialogs.connect("play_sound", self._on_play_sound)
+
# this are settings to be done before window show
self._init_preferences()
@@ -431,6 +434,15 @@ def __init__(self, argv):
self.GSTAT = Status()
self.GSTAT.connect("graphics-gcode-properties", self.on_gcode_properties)
self.GSTAT.connect("file-loaded", self.on_hal_status_file_loaded)
+ self.GSTAT.connect('macro-call-request', lambda w, name: self.request_macro_call(name))
+ self.GSTAT.connect('cycle-start-request', lambda w, state :self.request_start(state))
+ self.GSTAT.connect('cycle-pause-request', lambda w, state: self.request_pause(state))
+ self.GSTAT.connect('ok-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.ACCEPT))
+ self.GSTAT.connect('cancel-request', lambda w, state: self.dialogs.dialog_ext_control(Gtk.ResponseType.CANCEL))
+ self.GSTAT.connect('cancel-request', lambda w, state: self._del_notification())
+ self.GSTAT.connect('shutdown-request', lambda w: self.system_shutdown(w))
+ self.GSTAT.connect('softkey-pressed', lambda w,data: self.softkey_pressed(data))
+ self.GSTAT.connect('axis-selection-changed', lambda w,data: self.mpg_selection_changed(data))
# get if run from line should be used
self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str)
@@ -1314,6 +1326,70 @@ def _make_joints_button(self):
self.joints_button_dic[name] = btn
+ # halui/external cycle start request
+ def request_start(self,data):
+ print('start request')
+ if self.GSTAT.is_auto_paused():
+ self.command.auto(linuxcnc.AUTO_RESUME)
+ elif self.stat.task_mode == linuxcnc.MODE_MDI:
+ print('Submit MDI')
+ self.widgets.hal_mdihistory.submit()
+ elif self.stat.task_mode == linuxcnc.MODE_MANUAL:
+ self._show_error((13, _("Can't start cycles or submit MDI commands in manual Mode")))
+ else:
+ print('Cycle Start')
+ self.widgets.btn_run.emit('clicked')
+
+ # halui/external pause request
+ def request_pause(self,data):
+ print('pause request')
+
+ # don't toggle
+ if self.GSTAT.is_auto_paused():
+ return
+
+ if self.stat.task_mode == linuxcnc.MODE_AUTO:
+ self.widgets.tbtn_pause.emit('clicked')
+ else:
+ self.command.auto(linuxcnc.AUTO_PAUSE)
+
+ # call INI macro (from hal_glib message)
+ def request_macro_call(self, data):
+ # if MDI command change to MDI and run
+ cmd = self.INFO.get_ini_mdi_command(data)
+ print(f'MDI command:{cmd} data:{data}')
+ if not cmd is None:
+ LOG.debug("INI MDI COMMAND #: {} = {}".format(data, cmd))
+ self.ACTION.CALL_INI_MDI(data,mode_return = True)
+ return
+
+ # run Macros
+ # some error checking
+ if not self.GSTAT.is_mdi_mode():
+ message = _("You must be in MDI mode to run macros")
+ self.dialogs.show_warning_dialog( _("Important Warning!"),
+ message)
+ return
+
+ # look thru the INI macros
+ macros = self.get_ini_info.get_macros()
+ num_macros = len(macros)
+ if num_macros > 14:
+ num_macros = 14
+ for pos in range(0, num_macros):
+ # extract just the macro name
+ name = macros[pos].split()[0]
+ if data == name:
+ # get the button instance and click it
+ button = self["button_macro_{0}".format(pos)]
+ button.emit("clicked")
+ break
+ else:
+ # didn't match a name - give a hint
+ message = _("Macro {} not found ".format(data))
+ self.dialogs.show_warning_dialog( _("Important Warning!"),
+ message)
+
# check if macros are in the INI file and add them to MDI Button List
def _make_macro_button(self):
LOG.debug("Entering make macro button")
@@ -1355,7 +1431,9 @@ def _make_macro_button(self):
btn.set_halign(Gtk.Align.CENTER)
btn.set_valign(Gtk.Align.CENTER)
btn.set_property("name","macro_{0}".format(pos))
- btn.set_property("tooltip-text", _("Press to run macro {0}").format(name))
+ # keep a reference of the button
+ self["button_macro_{0}".format(pos)] = btn
+ btn.set_property("tooltip-text", _("Press to run macro {0}".format(name)))
btn.connect("clicked", self._on_btn_macro_pressed, name)
btn.position = pos
btn.show()
@@ -1488,7 +1566,8 @@ def _make_lathe(self):
message += _("this is not a lathe, as a lathe must have at least\n")
message += _("an X and an Z axis\n")
message += _("Wrong lathe configuration, we will leave here")
- self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False)
+ self.dialogs.show_warning_dialog( _("Very critical situation"),
+ message)
sys.exit()
else:
if not len(self.axis_list) == 2 and not len(self.axis_list) < 6:
@@ -1939,7 +2018,8 @@ def _init_tooleditor(self):
if not tooltable:
message = _("Did not find a toolfile file in [EMCIO] TOOL_TABLE")
LOG.error(message)
- self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False)
+ self.dialogs.show_warning_dialog( _("Very critical situation"),
+ message)
sys.exit()
toolfile = os.path.join(CONFIGPATH, tooltable)
self.widgets.tooledit1.set_filename(toolfile)
@@ -2096,15 +2176,15 @@ def on_tool_col_edit_started(self, widget, filtered_path, new_text, col):
toolpage = self.widgets.tooledit1
toolview = toolpage.view1
model, treeiter = toolview.get_selection().get_selected()
- value = self.dialogs.entry_dialog(self,
- data=model[treeiter][col],
+ value = self.dialogs.show_entry_dialog(data=model[treeiter][col],
header=_("Enter value"),
label=_("Tool") + f" {model[treeiter][1]}, {captations[col]}:",
integer=col in [1,2,15])
+
if value == "ERROR":
LOG.debug("conversion error")
- self.dialogs.warning_dialog(self, _("Conversion error !"),
- ("Please enter only numerical values\nValues have not been applied"))
+ self.dialogs.warning_dialog(_("Conversion error !"),
+ ("Please enter only numerical values\nValues have not been applied"))
elif value == "CANCEL":
pass
else:
@@ -2399,7 +2479,8 @@ def _init_offsetpage(self):
if not parameterfile:
message = _("Did not find a parameter file in [RS274NGC] PARAMETER_FILE")
LOG.error(message)
- self.dialogs.warning_dialog(self, _("Very critical situation"), message, sound = False)
+ self.dialogs.show_warning_dialog( _("Very critical situation"),
+ message)
sys.exit()
path = os.path.join(CONFIGPATH, parameterfile)
self.widgets.offsetpage1.set_filename(path)
@@ -2498,12 +2579,13 @@ def on_offset_col_edit_started(self, widget, filtered_path, new_text, col):
path = offsetpage.modelfilter.get_path(treeiter)
(store_path,) = offsetpage.modelfilter.convert_path_to_child_path(path)
row = store_path
- if self.widgets.offsetpage1.btn_edit_offsets.get_active():
- offset = self.dialogs.entry_dialog(self,
- data=offsetpage.store[row][col],
+ if self.widgets.offsetpage1.btn_edit_offsets.get_active() or \
+ self.touch_button_dic["edit_offsets"].get_active():
+ value = self.dialogs.show_entry_dialog(data=offsetpage.store[row][col],
header=_("Enter value for offset"),
label=f"{offsetpage.store[row][0]} {AXISLIST[col]}-" + _("offset:"),
integer=False)
+
if offset == "ERROR":
LOG.debug("conversion error")
self.dialogs.warning_dialog(self, _("Conversion error !"),
@@ -2655,7 +2737,7 @@ def _show_user_message(self, pin, message):
self.halcomp["messages." + message[2] + "-waiting"] = 1
self.halcomp["messages." + message[2] + "-response"] = 0
title = "Pin " + message[2] + " message"
- response = self.dialogs.yesno_dialog(self, message[0], title)
+ response = self.dialogs.show_yesno_dialog(self, message[0], title)
self.halcomp["messages." + message[2] + "-waiting"] = 0
self.halcomp["messages." + message[2] + "-response"] = response
else:
@@ -2856,6 +2938,9 @@ def on_rbt_auto_pressed(self, widget, data=None):
def on_btn_exit_clicked(self, widget, data=None):
self.widgets.window1.destroy()
+ def system_shutdown(self, widget):
+ self.ACTION.SHUT_SYSTEM_DOWN_PROMPT()
+
# button handlers End
# =========================================================
@@ -3163,13 +3248,14 @@ def on_hal_status_mode_mdi(self, widget):
self.last_key_event = None, 0
def on_mdi_calculation_start(self, *args):
- position = self.widgets.hal_mdihistory.entry.get_position()
- print("position: ", position)
- value = self.dialogs.entry_dialog(self,
+ value = self.dialogs.show_entry_dialog(
data=self.widgets.hal_mdihistory.entry.get_text(),
header=_("Enter value"),
label=_("Calculate value to insert"),
integer=False)
+
+ position = self.widgets.hal_mdihistory.entry.get_position()
+
if value == "ERROR":
LOG.debug("conversion error")
self.dialogs.warning_dialog(self, _("Conversion error !"),
@@ -3375,8 +3461,9 @@ def _on_btn_macro_pressed( self, widget = None, data = None ):
command = str( "O<" + o_codes[0] + "> call" )
for code in o_codes[1:]:
- parameter = self.dialogs.entry_dialog(self, data=None, header=_("Enter value:"),
+ parameter = self.dialogs.show_entry_dialog(data=None, header=_("Enter value:"),
label=f"{code}:", integer=False)
+
if parameter == "ERROR":
LOG.debug("conversion error")
self.dialogs.warning_dialog(self, _("Conversion error !"),
@@ -4229,8 +4316,12 @@ def on_tbtn_setup_toggled(self, widget, data=None):
code = True
# else we ask for the code using the system.dialog
if self.widgets.rbt_use_unlock.get_active():
- if self.dialogs.system_dialog(self):
- code = True
+ code = self.dialogs.show_system_dialog()
+ # cancelled?
+ if code == -1:
+ self.widgets.tbtn_setup.set_active(False)
+ widget.set_image(self.widgets.img_settings)
+ return
# Lets see if the user has the right to enter settings
if code:
self.widgets.ntb_main.set_current_page(1)
@@ -4242,7 +4333,7 @@ def on_tbtn_setup_toggled(self, widget, data=None):
message = _("Hal Pin is low, Access denied")
else:
message = _("wrong code entered, Access denied")
- self.dialogs.warning_dialog(self, _("Just to warn you"), message)
+ self.dialogs.show_warning_dialog(_("Just to warn you"), message)
self.widgets.tbtn_setup.set_active(False)
widget.set_image(self.widgets.img_settings)
else:
@@ -4398,8 +4489,8 @@ def on_btn_classicladder_clicked(self, widget, data=None):
if hal.component_exists("classicladder_rt"):
p = os.popen("classicladder &", "w")
else:
- self.dialogs.warning_dialog(self, _("INFO:"),
- _("Classicladder real-time component not detected"))
+ self.dialogs.show_warning_dialog(_("INFO:"),
+ _("Classicladder real-time component not detected"))
# =========================================================
# spindle stuff
@@ -4693,7 +4784,7 @@ def on_btn_delete_clicked(self, widget, data=None):
message = _("Do you really want to delete the MDI history?\n")
message += _("This will not delete the MDI History file, but will\n"
"delete the listbox entries for this session.")
- result = self.dialogs.yesno_dialog(self, message, _("Attention!!"))
+ result = self.dialogs.show_yesno_dialog(self, message, _("Attention!!"))
if result:
self.widgets.hal_mdihistory.model.clear()
@@ -4707,14 +4798,13 @@ def on_btn_show_calc_clicked(self, widget):
text = mdi_entry.get_text()
data = text[bounds[0]:bounds[1]]
has_selection = True
- value = self.dialogs.entry_dialog(self,
- data=data,
+ value = self.dialogs.show_entry_dialog(data=data,
header=_("Enter value"),
label=_("Calculate value to insert"),
integer=False)
if value == "ERROR":
LOG.debug("conversion error")
- self.dialogs.warning_dialog(self, _("Conversion error !"),
+ self.dialogs.show_warning_dialog(_("Conversion error !"),
("Please enter only numerical values\nValues have not been applied"))
elif value == "CANCEL":
return
@@ -4733,14 +4823,13 @@ def on_btn_show_calc_clicked(self, widget):
bounds = buffer.get_selection_bounds()
data = buffer.get_text(bounds[0],bounds[1],False)
has_selection = True
- value = self.dialogs.entry_dialog(self,
- data=data,
+ value = self.dialogs.show_entry_dialog(data=data,
header=_("Enter value"),
label=_("Calculate value to insert"),
integer=False)
if value == "ERROR":
LOG.debug("conversion error")
- self.dialogs.warning_dialog(self, _("Conversion error !"),
+ self.dialogs.show_warning_dialog(_("Conversion error !"),
("Please enter only numerical values\nValues have not been applied"))
elif value == "CANCEL":
return
@@ -4800,7 +4889,7 @@ def on_btn_back_clicked(self, widget, data=None):
if self.widgets.ntb_button.get_current_page() == _BB_EDIT: # edit mode, go back to auto_buttons
if self.file_changed:
message = _("Exit and discard changes?")
- result = self.dialogs.yesno_dialog(self, message, _("Attention!"))
+ result = self.dialogs.show_yesno_dialog(self, message, _("Attention!"))
if not result: # user says no, he want to save
return
self.widgets.ntb_button.set_current_page(_BB_AUTO)
@@ -4813,7 +4902,7 @@ def on_btn_back_clicked(self, widget, data=None):
else: # else we go to main button on manual
if self.widgets.tooledit1.edited:
message = _("Discard unsaved changes and exit?")
- result = self.dialogs.yesno_dialog(self, message, _("Attention!"))
+ result = self.dialogs.show_yesno_dialog(self, message, _("Attention!"))
if not result: # user says no, he want to save
return
# check if offset values for current tool have been changed
@@ -4835,7 +4924,7 @@ def on_btn_back_clicked(self, widget, data=None):
"Do you want to activate tool compensation (G43)\n" \
"using the currently active tool offset?")
if message:
- result = self.dialogs.yesno_dialog(self, message, _("Attention!"))
+ result = self.dialogs.show_yesno_dialog(self, message, _("Attention!"))
if result: # user says YES
self.command.mode(linuxcnc.MODE_MDI)
self.command.wait_complete()
@@ -4913,22 +5002,22 @@ def _on_btn_set_value_clicked(self, widget, data=None):
if self.lathe_mode and axis =="x":
if self.diameter_mode:
preset = self.prefs.getpref("diameter offset_axis_{0}".format(axis), 0, float)
- offset = self.dialogs.entry_dialog(self, data=preset, header=_("Enter value for diameter"),
+ offset = self.dialogs.show_entry_dialog(data=preset, header=_("Enter value for diameter"),
label=_("Set diameter to:"), integer=False)
else:
preset = self.prefs.getpref("radius offset_axis_{0}".format(axis), 0, float)
- offset = self.dialogs.entry_dialog(self, data=preset, header=_("Enter value for radius"),
+ offset = self.dialogs.show_entry_dialog(data=preset, header=_("Enter value for radius"),
label=_("Set radius to:"), integer=False)
else:
preset = self.prefs.getpref("offset_axis_{0}".format(axis), 0, float)
- offset = self.dialogs.entry_dialog(self, data=preset, header=_("Enter value for axis {0}").format(axis.upper()),
+ offset = self.dialogs.show_entry_dialog(data=preset, header=_("Enter value for axis {0}").format(axis.upper()),
label=_("Set axis {0} to:").format(axis.upper()), integer=False)
if offset == "CANCEL":
return
elif offset == "ERROR":
LOG.debug("Conversion error in btn_set_value")
- self.dialogs.warning_dialog(self, _("Conversion error in btn_set_value!"),
- _("Please enter only numerical values. Values have not been applied"))
+ self.dialogs.show_warning_dialog(_("Conversion error in btn_set_value!"),
+ _("Please enter only numerical values\nValues have not been applied"))
else:
self.command.mode(linuxcnc.MODE_MDI)
self.command.wait_complete()
@@ -4949,7 +5038,8 @@ def _on_btn_set_selected_clicked(self, widget, data=None):
system, name = self.widgets.offsetpage1.get_selected()
if system not in ["G54", "G55", "G56", "G57", "G58", "G59", "G59.1", "G59.2", "G59.3"]:
message = _("You did not select a system to be changed to, so nothing will be changed")
- self.dialogs.warning_dialog(self, _("Important Warning!"), message)
+ self.dialogs.show_warning_dialog(_("Important Warning!"),
+ message)
return
if system == self.system_list[self.stat.g5x_index]:
return
@@ -4999,7 +5089,7 @@ def on_chk_reload_tool_toggled(self, widget, data=None):
def on_btn_block_height_clicked(self, widget, data=None):
probeheight = self.widgets.spbtn_probe_height.get_value()
preset = self.prefs.getpref("blockheight", 0.0, float)
- blockheight = self.dialogs.entry_dialog(self, data=preset, header=_("Enter the block height"),
+ blockheight = self.dialogs.show_entry_dialog(data=preset, header=_("Enter the block height"),
label=_("Block height measured from base table"), integer=False)
if blockheight == "CANCEL" or blockheight == "ERROR":
@@ -5012,8 +5102,8 @@ def on_btn_block_height_clicked(self, widget, data=None):
else:
self.prefs.putpref("blockheight", 0.0, float)
self.prefs.putpref("probeheight", 0.0, float)
- self.dialogs.warning_dialog(self, _("Conversion error in btn_block_height!"),
- _("Please enter only numerical values\nValues have not been applied"))
+ self.dialogs.show_warning_dialog(_("Conversion error in btn_block_height!"),
+ _("Please enter only numerical values\nValues have not been applied"))
# set coordinate system to new origin
origin = self.get_ini_info.get_axis_2_min_limit() + blockheight
@@ -5041,7 +5131,9 @@ def _set_icon_theme(self, name):
if name is None or name == "none":
# Switching to none required a restart (skip entire icon theme stuff)
message = "Change to no icon theme requires a restart to take effect."
- self.dialogs.warning_dialog(self, _("Just to warn you"), message)
+ self.dialogs.show_warning_dialog( _("Just to warn you"),
+ message)
+
else:
self.icon_theme.set_custom_theme(name)
self.notification.set_property('icon_theme_name', name)
@@ -5585,8 +5677,11 @@ def on_tool_change(self, widget):
except:
message = _("Tool\n\n# {0:d}\n\n not in the tool table!").format(toolnumber)
- result = self.dialogs.warning_dialog(self, message, title=_("Manual Tool change"),\
- confirm_pin = 'toolchange-confirm', active_pin = 'toolchange-change')
+ result = self.dialogs.show_warning_dialog( _("Manual Tool change"),
+ message, context='mantoolchange',
+ confirm_pin = 'toolchange-confirm',
+ active_pin = 'toolchange-change')
+
if result:
self.halcomp["toolchange-changed"] = True
else:
@@ -5597,16 +5692,19 @@ def on_tool_change(self, widget):
self.halcomp['toolchange-changed'] = True
message = _("Tool Change has been aborted!\n")
message += _("The old tool will remain set!")
- self.dialogs.warning_dialog(self, message)
+ self.dialogs.show_warning_dialog( _("Just to warn you"),
+ message)
+
else:
self.halcomp['toolchange-changed'] = False
def on_btn_delete_tool_clicked(self, widget, data=None):
selected_tool = self.widgets.tooledit1.get_selected_row()
if self.stat.tool_in_spindle == selected_tool:
- message = _("You are trying to delete the tool mounted in the spindle.\n"
- "This is not allowed, please change tool prior to delete it.")
- self.dialogs.warning_dialog(self, _("Warning Tool can not be deleted!"), message)
+ message = _("You are trying to delete the tool mounted in the spindle\n")
+ message += _("This is not allowed, please change tool prior to delete it")
+ self.dialogs.show_warning_dialog( _("Warning Tool can not be deleted!"),
+ message)
return
self.widgets.tooledit1.delete_selected_row(widget)
self.widgets.tooledit1.edited = True
@@ -5618,7 +5716,7 @@ def on_btn_add_tool_clicked(self, widget, data=None):
def on_btn_reload_tooltable_clicked(self, widget, data=None):
if self.widgets.tooledit1.edited:
message = _("Discard unsaved changes and reload the table?")
- result = self.dialogs.yesno_dialog(self, message, _("Attention!"))
+ result = self.dialogs.show_yesno_dialog(self, message, _("Attention!"))
if not result: # user says no, he want to save
return
self.widgets.tooledit1.reload(None)
@@ -5634,13 +5732,15 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None):
if not tool:
message = _("No tool selected in the tool table. ")
message += _("Please select only one tool in the table!")
- self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message)
+ self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"),
+ message)
return
if tool != self.stat.tool_in_spindle:
message = _("You can not touch off a tool, which is not mounted in the spindle! ")
message += _("Your selection has been reset to the tool in spindle.")
- self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message)
+ self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"),
+ message)
self.widgets.tooledit1.reload(self)
self.widgets.tooledit1.set_selected_tool(self.stat.tool_in_spindle)
return
@@ -5648,7 +5748,8 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None):
if "G41" in self.active_gcodes or "G42" in self.active_gcodes:
message = _("Tool touch off is not possible with cutter radius compensation switched on!\n")
message += _("Please emit an G40 before tool touch off.")
- self.dialogs.warning_dialog(self, _("Warning Tool Touch off not possible!"), message)
+ self.dialogs.show_warning_dialog( _("Warning Tool Touch off not possible!"),
+ message)
return
if widget == self.widgets.btn_tool_touchoff_x:
@@ -5656,18 +5757,19 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None):
elif widget == self.widgets.btn_tool_touchoff_z:
axis = "z"
else:
- self.dialogs.warning_dialog(self, _("Real big error!"),
- _("You managed to come to a place that is not possible in on_btn_tool_touchoff"))
+ self.dialogs.show_warning_dialog(_("Real big error!"),
+ _("You managed to come to a place that is not possible in on_btn_tool_touchoff"))
return
- value = self.dialogs.entry_dialog(self, data=None,
+ value = self.dialogs.show_entry_dialog(data=None,
header=_("Enter value for axis {0} to set:").format(axis.upper()),
label=_("Set parameter of tool {0:d} and axis {1} to:").format(self.stat.tool_in_spindle, axis.upper()),
integer=False)
if value == "ERROR":
message = _("Conversion error because of wrong entry for touch off axis {0}").format(axis.upper())
- self.dialogs.warning_dialog(self, _("Conversion error !"), message)
+ self.dialogs.show_warning_dialog( _("Conversion error !"),
+ message)
return
elif value == "CANCEL":
return
@@ -5687,21 +5789,23 @@ def on_btn_tool_touchoff_clicked(self, widget, data=None):
def on_btn_select_tool_by_no_clicked(self, widget, data=None):
if self.widgets.tooledit1.edited:
message = _("Discard unsaved changes and change tool?")
- result = self.dialogs.yesno_dialog(self, message, _("Attention!"))
+ result = self.dialogs.show_yesno_dialog(self, message, _("Attention!"))
if not result: # user says no, he want to save
return
- value = self.dialogs.entry_dialog(self, data=None, header=_("Enter the tool number as integer "),
+ value = self.dialogs.show_entry_dialog(data=None, header=_("Enter the tool number as integer "),
label=_("Select the tool to change"), integer=True)
if value == "ERROR":
message = _("Conversion error because of wrong entry for tool number.\n")
message += _("Enter only integer numbers!")
- self.dialogs.warning_dialog(self, _("Conversion error !"), message)
+ self.dialogs.show_warning_dialog( _("Conversion error !"),
+ message)
return
elif value == "CANCEL":
return
elif int(value) == self.stat.tool_in_spindle:
message = _("Selected tool is already in spindle, no change needed.")
- self.dialogs.warning_dialog(self, _("Important Warning!"), message)
+ self.dialogs.show_warning_dialog( _("Important Warning!"),
+ message)
return
else:
self.tool_change = True
@@ -5716,17 +5820,19 @@ def on_btn_select_tool_by_no_clicked(self, widget, data=None):
def on_btn_selected_tool_clicked(self, widget, data=None):
if self.widgets.tooledit1.edited:
message = _("Discard unsaved changes and change tool?")
- result = self.dialogs.yesno_dialog(self, message, _("Attention!"))
+ result = self.dialogs.show_yesno_dialog(self, message, _("Attention!"))
if not result: # user says no, he want to save
return
tool = self.widgets.tooledit1.get_selected_row()
if tool == None:
message = _("you selected no or more than one tool, the tool selection must be unique")
- self.dialogs.warning_dialog(self, _("Important Warning!"), message)
+ self.dialogs.show_warning_dialog( _("Important Warning!"),
+ message)
return
if tool == self.stat.tool_in_spindle:
message = _("Selected tool is already in spindle, no change needed.")
- self.dialogs.warning_dialog(self, _("Important Warning!"), message)
+ self.dialogs.show_warning_dialog( _("Important Warning!"),
+ message)
return
if tool or tool == 0:
self.tool_change = True
@@ -5744,7 +5850,9 @@ def on_btn_selected_tool_clicked(self, widget, data=None):
self.command.mdi(command)
else:
message = _("Could not understand the entered tool number. Will not change anything!")
- self.dialogs.warning_dialog(self, _("Important Warning!"), message)
+ self.dialogs.show_warning_dialog( _("Important Warning!"),
+ message)
+
# =========================================================
# gremlin relevant calls
@@ -6238,6 +6346,9 @@ def _on_message_deleted(self, widget, messages, checkbox_checked):
def _del_message_changed(self, pin):
if pin.get():
+ self._del_notification()
+
+ def _del_notification(self):
if self.halcomp["error"] == True:
number = []
messages = self.notification.messages
@@ -6295,7 +6406,46 @@ def _blockdelete(self, pin):
LOG.debug("Received a signal from pin {0} with state = {1}".format(pin.name, pin.get()))
self.command.set_optional_stop(pin.get())
+ # from gmoccapy's MPG input pin
+ def _external_mpg(self, pin):
+ count = pin.get()
+ diff = count - self._last_count
+ if self.mpg_enabled:
+ if diff <0:
+ if self.GSTAT.is_auto_mode() and not self.GSTAT.is_auto_running():
+ self.widgets.gcode_view.line_up()
+ else:
+ self.widgets.gremlin.zoom_out()
+ else:
+ if self.GSTAT.is_auto_mode() and not self.GSTAT.is_auto_running():
+ self.widgets.gcode_view.line_down()
+ self.widgets.gcode_view.line_down()
+ else:
+ self.widgets.gremlin.zoom_in()
+ self._last_count = count
+
+ # enabled externally by halui/hal_bridge
+ def mpg_selection_changed(self, data):
+ if data =='MPG0':
+ self.mpg_enabled = True
+ else:
+ self.mpg_enabled = False
+
# =========================================================
+ # external request for a softkey press from HALUI/halbridge
+ def softkey_pressed(self, index):
+ if index > 9:
+ location = "bottom"
+ number = index -10
+ elif index < 7:
+ location = "right"
+ number = index
+ else:
+ LOG.debug(f"Could not translate softkey {index} to button number")
+ LOG.debug(f"softkey index: {index}, location: {location}, number: {number}")
+ button = self._get_child_button(location, number)
+ self.process_button(button)
+
# The actions of the buttons
def _button_pin_changed(self, pin):
# we check if the button is pressed or released,
@@ -6319,6 +6469,9 @@ def _button_pin_changed(self, pin):
return
button = self._get_child_button(location, number)
+ self.process_button(button)
+
+ def process_button(self, button):
if not button:
LOG.debug("no button here")
return
@@ -6512,6 +6665,18 @@ def _make_hal_pins(self):
pin = self.halcomp.newpin("blockdelete", hal.HAL_BIT, hal.HAL_IN)
hal_glib.GPin(pin).connect("value_changed", self._blockdelete)
+ pin = self.halcomp.newpin('mpg-in',hal.HAL_S32, hal.HAL_IN)
+ hal_glib.GPin(pin).connect("value_changed", self._external_mpg)
+
+ ##############################
+ # required class boiler code #
+ # for subscriptable objects #
+ ##############################
+ def __getitem__(self, item):
+ return getattr(self, item)
+
+ def __setitem__(self, item, value):
+ return setattr(self, item, value)
# Hal Pin Handling End
# =========================================================
@@ -6552,6 +6717,7 @@ def _make_hal_pins(self):
# Some of these libraries log when imported so logging level must already be set.
import gladevcp.makepins
from gladevcp.core import Info, Status
+ from gladevcp.core import Info, Status, Action
from gladevcp.combi_dro import Combi_DRO # we will need it to make the DRO
from gmoccapy import widgets # a class to handle the widgets
diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc
index 4e1761f64d9..70cdd2b0746 100644
--- a/src/emc/usr_intf/halui.cc
+++ b/src/emc/usr_intf/halui.cc
@@ -43,6 +43,10 @@
using namespace linuxcnc;
+#define PY_SSIZE_T_CLEAN
+#include
+#include
+
/* Using halui: see the man page */
static int axis_mask = 0;
@@ -50,6 +54,7 @@ static int axis_mask = 0;
#define JOGTELEOP 0
#define MDI_MAX 64
+#define SOFTKEY_MAX 20
#pragma GCC diagnostic push
#if defined(__GNUC__) && (__GNUC__ > 4)
@@ -86,6 +91,8 @@ static int axis_mask = 0;
FIELD(hal_bit_t,program_is_running) /* pin for notifying user that program is running */ \
FIELD(hal_bit_t,halui_mdi_is_running) /* pin for notifying user that halui MDI commands is running */ \
FIELD(hal_bit_t,program_is_paused) /* pin for notifying user that program is paused */ \
+ FIELD(hal_bit_t,cycle_start) /* pin for running program */ \
+ FIELD(hal_bit_t,cycle_pause) /* pin for running program */ \
FIELD(hal_bit_t,program_run) /* pin for running program */ \
FIELD(hal_bit_t,program_pause) /* pin for pausing program */ \
FIELD(hal_bit_t,program_resume) /* pin for resuming program */ \
@@ -98,7 +105,7 @@ static int axis_mask = 0;
FIELD(hal_bit_t,program_bd_off) /* pin for setting block delete off */ \
FIELD(hal_bit_t,program_bd_is_on) /* status pin that block delete is on */ \
\
- FIELD(hal_u32_t,tool_number) /* pin for current selected tool */ \
+ FIELD(hal_s32_t,tool_number) /* pin for current selected tool */ \
FIELD(hal_float_t,tool_length_offset_x) /* current applied x tool-length-offset */ \
FIELD(hal_float_t,tool_length_offset_y) /* current applied y tool-length-offset */ \
FIELD(hal_float_t,tool_length_offset_z) /* current applied z tool-length-offset */ \
@@ -133,11 +140,12 @@ static int axis_mask = 0;
ARRAY(hal_bit_t,joint_on_hard_max_limit,EMCMOT_MAX_JOINTS+1) /* status pin that the joint is on the hardware max limit */ \
ARRAY(hal_bit_t,joint_override_limits,EMCMOT_MAX_JOINTS+1) /* status pin that the joint is on the hardware max limit */ \
ARRAY(hal_bit_t,joint_has_fault,EMCMOT_MAX_JOINTS+1) /* status pin that the joint has a fault */ \
- FIELD(hal_u32_t,joint_selected) /* status pin for the joint selected */ \
- FIELD(hal_u32_t,axis_selected) /* status pin for the axis selected */ \
+ FIELD(hal_s32_t,joint_selected) /* status pin for the joint selected */ \
+ FIELD(hal_s32_t,axis_selected) /* status pin for the axis selected */ \
\
ARRAY(hal_bit_t,joint_nr_select,EMCMOT_MAX_JOINTS) /* nr. of pins to select a joint */ \
ARRAY(hal_bit_t,axis_nr_select,EMCMOT_MAX_AXIS) /* nr. of pins to select a axis */ \
+ FIELD(hal_bit_t,mpg_select0)\
\
ARRAY(hal_bit_t,joint_is_selected,EMCMOT_MAX_JOINTS) /* nr. of status pins for joint selected */ \
ARRAY(hal_bit_t,axis_is_selected,EMCMOT_MAX_AXIS) /* nr. of status pins for axis selected */ \
@@ -155,6 +163,7 @@ static int axis_mask = 0;
ARRAY(hal_bit_t,jjog_increment_minus,EMCMOT_MAX_JOINTS+1) /* Incremental jogging, negative direction */ \
\
FIELD(hal_float_t,ajog_speed) /* pin for setting the jog speed (halui internal) */ \
+ FIELD(hal_float_t,ajog_speed_angular) /* pin for setting the angular jog speed (halui internal) */ \
ARRAY(hal_bit_t,ajog_minus,EMCMOT_MAX_AXIS+1) /* pin to jog in positive direction */ \
ARRAY(hal_bit_t,ajog_plus,EMCMOT_MAX_AXIS+1) /* pin to jog in negative direction */ \
ARRAY(hal_float_t,ajog_analog,EMCMOT_MAX_AXIS+1) /* pin for analog jogging (-1..0..1) */ \
@@ -202,7 +211,15 @@ static int axis_mask = 0;
\
FIELD(hal_bit_t,home_all) /* pin for homing all joints in sequence */ \
FIELD(hal_bit_t,abort) /* pin for aborting */ \
+\
ARRAY(hal_bit_t,mdi_commands,MDI_MAX) \
+ ARRAY(hal_bit_t,gui_mdi_commands,MDI_MAX) \
+ ARRAY(hal_bit_t,gui_soft_keys,SOFTKEY_MAX) \
+\
+ FIELD(hal_bit_t,gui_ok) /* pin for acknowledging dialog ok */ \
+ FIELD(hal_bit_t,gui_cancel) /* pin for acknowledging dialog cancel */ \
+ FIELD(hal_bit_t,gui_reload) /* pin for acknowledging dialog ok */ \
+ FIELD(hal_bit_t,gui_shutdown) /* pin for acknowledging dialog cancel */ \
\
FIELD(hal_float_t,units_per_mm) \
@@ -234,13 +251,28 @@ typedef halui_str_base halui_str;
typedef halui_str_base local_halui_str;
#pragma GCC diagnostic pop
+PyObject *pModule, *pFuncRead, *pFuncWrite, *pInstance, *pClass;
+PyObject *pValue;
+
static halui_str *halui_data;
static local_halui_str old_halui_data;
+static double lastjogspeed = 0;
+static double internaljogspeed = 0;
+static double lastangularjogspeed = 0;
+static double internalangularjogspeed = 0;
+static double tempjogspeed = 0;
+static int lastaxis = -1;
+
static char *mdi_commands[MDI_MAX];
static int num_mdi_commands=0;
+
+static char *gui_mdi_commands[MDI_MAX];
+static int num_gui_mdi_commands = 0;
static int have_home_all = 0;
+static int num_gui_soft_keys = SOFTKEY_MAX;
+
static int comp_id, done; /* component ID, main while loop */
static int num_axes = 0; //number of axes, taken from the INI [TRAJ] section
@@ -531,6 +563,151 @@ int halui_export_pin_OUT_bit(hal_bit_t **pin, const char *name)
return 0;
}
+static void py_call_cycleStart() {
+
+ // check socket messages for jogspeed
+ pFuncWrite = PyObject_GetAttrString(pInstance, "cycleStart");
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallNoArgs(pFuncWrite);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: cycleStart function failed: returned NULL\n");
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncWrite);
+ return ;
+}
+
+static void py_call_cyclePause() {
+
+ // check socket messages for jogspeed
+ pFuncWrite = PyObject_GetAttrString(pInstance, "cyclePause");
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallNoArgs(pFuncWrite);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: cyclePause function failed: returned NULL\n");
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncWrite);
+ return ;
+}
+
+static void py_call_ok() {
+
+ // check socket messages for gui ok message
+ pFuncWrite = PyObject_GetAttrString(pInstance, "ok");
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallNoArgs(pFuncWrite);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: ok function failed: returned NULL\n");
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncWrite);
+ return ;
+}
+static void py_call_cancel() {
+
+ // check socket messages for gui cancel message
+ pFuncWrite = PyObject_GetAttrString(pInstance, "cancel");
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallNoArgs(pFuncWrite);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: cancel function failed: returned NULL\n");
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncWrite);
+ return ;
+}
+static void py_call_reload_display() {
+
+ // check socket messages for gui reload display message
+ pFuncWrite = PyObject_GetAttrString(pInstance, "reloadDisplay");
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallNoArgs(pFuncWrite);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: reload display function failed: returned NULL\n");
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncWrite);
+ return ;
+}
+static void py_call_shutdown_controller() {
+
+ // check socket messages for gui shutdown message
+ pFuncWrite = PyObject_GetAttrString(pInstance, "shutdownController");
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallNoArgs(pFuncWrite);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: shutdownController function failed: returned NULL\n");
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncWrite);
+ return ;
+}
+static int py_call_get_mdi_count() {
+ int value = 0;
+ // check socket messages for jogspeed
+ pFuncRead = PyObject_GetAttrString(pInstance, "getMdiCount");
+ if (pFuncRead && PyCallable_Check(pFuncRead)) {
+ pValue = PyObject_CallNoArgs(pFuncRead);
+ if (pValue == NULL){
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "Halui Bridge: getMdiCountfunction failed: returned NULL\n");
+ value = -1;
+ }else{
+ if (PyLong_Check(pValue)) {
+ value = (int) PyLong_AsLong(pValue);
+ //fprintf(stderr, "axis value %d\n",value);
+ if (PyErr_Occurred()) {
+ value = -1;
+ // Handle conversion error
+ PyErr_Print();
+ // Clear the error state if needed
+ PyErr_Clear();
+ }
+ }
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncRead);
+ return value;
+}
+
+// turns a index number into a macro name
+static char* py_call_get_mdi_name( int num) {
+ pFuncWrite = PyObject_GetAttrString(pInstance, "getMdiName");
+
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallFunction(pFuncWrite, "i", num);
+
+ if (pValue != NULL) {
+ if (PyUnicode_Check(pValue)) {
+ PyObject *pBytes = PyUnicode_AsUTF8String(pValue);
+ char *result_string = PyBytes_AsString(pBytes);
+ Py_XDECREF(pBytes);
+ printf("Python function returned: %s %i\n", result_string,num);
+ Py_DECREF(pValue);
+ Py_DECREF(pFuncWrite);
+ return result_string;
+ } else {
+ fprintf(stderr, "Return value is not a string. %i\n", num);
+ }
+ } else {
+ PyErr_Print(); // Print Python error if call failed
+ }
+ Py_DECREF(pValue);
+ }else{
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "halui Bridge: Failed python function");
+ }
+ Py_DECREF(pFuncWrite);
+ return NULL;
+}
/********************************************************************
*
@@ -708,11 +885,11 @@ int halui_hal_init(void)
if (retval < 0) return retval;
retval = hal_pin_float_newf(HAL_OUT, &(halui_data->ro_value), comp_id, "halui.rapid-override.value");
if (retval < 0) return retval;
- retval = hal_pin_u32_newf(HAL_OUT, &(halui_data->joint_selected), comp_id, "halui.joint.selected");
+ retval = hal_pin_s32_newf(HAL_OUT, &(halui_data->joint_selected), comp_id, "halui.joint.selected");
if (retval < 0) return retval;
- retval = hal_pin_u32_newf(HAL_OUT, &(halui_data->axis_selected), comp_id, "halui.axis.selected");
+ retval = hal_pin_s32_newf(HAL_OUT, &(halui_data->axis_selected), comp_id, "halui.axis.selected");
if (retval < 0) return retval;
- retval = hal_pin_u32_newf(HAL_OUT, &(halui_data->tool_number), comp_id, "halui.tool.number");
+ retval = hal_pin_s32_newf(HAL_OUT, &(halui_data->tool_number), comp_id, "halui.tool.number");
if (retval < 0) return retval;
retval = hal_pin_float_newf(HAL_OUT, &(halui_data->tool_length_offset_x), comp_id, "halui.tool.length_offset.x");
if (retval < 0) return retval;
@@ -763,6 +940,10 @@ int halui_hal_init(void)
if (retval < 0) return retval;
retval = halui_export_pin_IN_bit(&(halui_data->flood_off), "halui.flood.off");
if (retval < 0) return retval;
+ retval = halui_export_pin_IN_bit(&(halui_data->cycle_start), "halui.cycle.start");
+ if (retval < 0) return retval;
+ retval = halui_export_pin_IN_bit(&(halui_data->cycle_pause), "halui.cycle.pause");
+ if (retval < 0) return retval;
retval = halui_export_pin_IN_bit(&(halui_data->program_run), "halui.program.run");
if (retval < 0) return retval;
retval = halui_export_pin_IN_bit(&(halui_data->program_pause), "halui.program.pause");
@@ -881,6 +1062,9 @@ int halui_hal_init(void)
if (retval < 0) return retval;
}
+ retval = halui_export_pin_IN_bit(&(halui_data->mpg_select0), "halui.mpg-select.0");
+ if (retval < 0) return retval;
+
retval = hal_pin_bit_newf(HAL_IN, &(halui_data->joint_home[num_joints]), comp_id, "halui.joint.selected.home");
if (retval < 0) return retval;
retval = hal_pin_bit_newf(HAL_IN, &(halui_data->joint_unhome[num_joints]), comp_id, "halui.joint.selected.unhome");
@@ -913,6 +1097,8 @@ int halui_hal_init(void)
retval = halui_export_pin_IN_float(&(halui_data->ajog_speed), "halui.axis.jog-speed");
if (retval < 0) return retval;
+ retval = halui_export_pin_IN_float(&(halui_data->ajog_speed_angular), "halui.axis.jog-speed-angular");
+ if (retval < 0) return retval;
retval = halui_export_pin_IN_float(&(halui_data->ajog_deadband), "halui.axis.jog-deadband");
if (retval < 0) return retval;
@@ -921,6 +1107,29 @@ int halui_hal_init(void)
if (retval < 0) return retval;
}
+ for (int n=0; ngui_mdi_commands[n]), comp_id, "halui.gui.mdi-command-%s", py_call_get_mdi_name(n));
+ if (retval < 0) return retval;
+ }
+
+ for (int n=0; ngui_soft_keys[n]), comp_id, "halui.gui.softkey-%d", n);
+ if (retval < 0) return retval;
+ }
+
+ retval = halui_export_pin_IN_bit(&(halui_data->gui_ok), "halui.gui.ok");
+ if (retval < 0) return retval;
+
+ retval = halui_export_pin_IN_bit(&(halui_data->gui_cancel), "halui.gui.cancel");
+ if (retval < 0) return retval;
+
+ retval = halui_export_pin_IN_bit(&(halui_data->gui_reload), "halui.gui.reload-display");
+ if (retval < 0) return retval;
+
+ retval = halui_export_pin_IN_bit(&(halui_data->gui_shutdown), "halui.gui.shutdown");
+ if (retval < 0) return retval;
+
hal_ready(comp_id);
return 0;
}
@@ -1261,6 +1470,7 @@ static void sendJogStop(int ja, int jjogmode)
static void sendJogCont(int ja, double speed, int jjogmode)
{
+
EMC_JOG_CONT emc_jog_cont_msg;
if (emcStatus->task.state != EMC_TASK_STATE::ON) { return; }
@@ -1489,6 +1699,12 @@ static int iniLoad(const char *filename)
mdi_commands[num_mdi_commands++] = strdup(mc->c_str());
}
+ int temp = py_call_get_mdi_count();
+ for (int n=0; naxis_nr_select[axis_num]) = old_halui_data.axis_nr_select[axis_num] = 0;
+ *(halui_data->mpg_select0) = old_halui_data.mpg_select0 = 0;
*(halui_data->ajog_minus[axis_num]) = old_halui_data.ajog_minus[axis_num] = 0;
*(halui_data->ajog_plus[axis_num]) = old_halui_data.ajog_plus[axis_num] = 0;
*(halui_data->ajog_analog[axis_num]) = old_halui_data.ajog_analog[axis_num] = 0;
@@ -1543,9 +1760,10 @@ static void hal_init_pins()
*(halui_data->ajog_increment_minus[EMCMOT_MAX_AXIS]) = old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS] = 0;
*(halui_data->ajog_deadband) = 0.2;
*(halui_data->ajog_speed) = 0;
+ *(halui_data->ajog_speed_angular) = 0;
*(halui_data->joint_selected) = 0; // select joint 0 by default
- *(halui_data->axis_selected) = 0; // select axis 0 by default
+ *(halui_data->axis_selected) = -1; // select no axis by default
*(halui_data->fo_scale) = old_halui_data.fo_scale = 0.1; //sane default
*(halui_data->ro_scale) = old_halui_data.ro_scale = 0.1; //sane default
@@ -1600,6 +1818,211 @@ static bool jogging_selected_axis(local_halui_str &hal) {
return (hal.ajog_plus[EMCMOT_MAX_AXIS] || hal.ajog_minus[EMCMOT_MAX_AXIS]);
}
+static double py_call_axis_get_jogspeed() {
+ double jspd = 0;
+ // check socket messages for jogspeed
+ pFuncRead = PyObject_GetAttrString(pInstance, "getJogRate");
+ if (pFuncRead && PyCallable_Check(pFuncRead)) {
+ pValue = PyObject_CallNoArgs(pFuncRead);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: getJogRate function failed: returned NULL\n");
+ jspd = 0;
+ }else{
+ if (PyFloat_Check(pValue)) {
+ jspd = PyFloat_AsDouble(pValue);
+ if (PyErr_Occurred()) {
+ jspd = 0;
+ // Handle conversion error
+ PyErr_Print();
+ // Clear the error state if needed
+ PyErr_Clear();
+ }
+ }
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncRead);
+ return jspd;
+}
+
+static void py_call_axis_jogspeed(double speed)
+{
+ pFuncWrite = PyObject_GetAttrString(pInstance, "setJogRate");
+
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallFunction(pFuncWrite, "d", speed);
+ if (pValue == NULL){
+ fprintf(stderr, "halui bridge: writeMsg function failed: returned NULL\n");
+ if (PyErr_Occurred()) PyErr_Print();
+ }
+ Py_DECREF(pValue);
+
+ }else{
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "halui Bridge: Failed python function");
+ }
+ Py_DECREF(pFuncWrite);
+}
+
+static double py_call_axis_get_angular_jogspeed() {
+ double jspd = 0;
+ // check socket messages for angular jogspeed
+ pFuncRead = PyObject_GetAttrString(pInstance, "getJogRateAngular");
+ if (pFuncRead && PyCallable_Check(pFuncRead)) {
+ pValue = PyObject_CallNoArgs(pFuncRead);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: getJogRateAngular function failed: returned NULL\n");
+ jspd = 0;
+ }else{
+ if (PyFloat_Check(pValue)) {
+ jspd = PyFloat_AsDouble(pValue);
+ if (PyErr_Occurred()) {
+ jspd = 0;
+ // Handle conversion error
+ PyErr_Print();
+ // Clear the error state if needed
+ PyErr_Clear();
+ }
+ }
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncRead);
+ return jspd;
+}
+
+static void py_call_axis_angular_jogspeed(double speed)
+{
+ pFuncWrite = PyObject_GetAttrString(pInstance, "setJogRateAngular");
+
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallFunction(pFuncWrite, "d", speed);
+ if (pValue == NULL){
+ fprintf(stderr, "halui bridge: setJogRateAngular function failed: returned NULL\n");
+ if (PyErr_Occurred()) PyErr_Print();
+ }
+ Py_DECREF(pValue);
+
+ }else{
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "halui Bridge: Failed python function");
+ }
+ Py_DECREF(pFuncWrite);
+}
+
+static int py_call_get_axis_selected() {
+ int value = 0;
+ // check socket messages for selected axis
+ pFuncRead = PyObject_GetAttrString(pInstance, "getSelectedAxis");
+ if (pFuncRead && PyCallable_Check(pFuncRead)) {
+ pValue = PyObject_CallNoArgs(pFuncRead);
+ if (pValue == NULL){
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "Halui Bridge: getSelectAxis function failed: returned NULL\n");
+ value = -1;
+ }else{
+ if (PyLong_Check(pValue)) {
+ value = (int) PyLong_AsLong(pValue);
+ //fprintf(stderr, "axis value %d\n",value);
+ if (PyErr_Occurred()) {
+ value = -1;
+ // Handle conversion error
+ PyErr_Print();
+ // Clear the error state if needed
+ PyErr_Clear();
+ }
+ }
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncRead);
+ return value;
+}
+
+static void py_call_axis_changed( int axis)
+{
+ pFuncWrite = PyObject_GetAttrString(pInstance, "setSelectedAxis");
+
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallFunction(pFuncWrite, "i", axis);
+ if (pValue == NULL){
+ fprintf(stderr, "halui bridge: writeMsg function failed: returned NULL\n");
+ if (PyErr_Occurred()) PyErr_Print();
+ }
+ Py_DECREF(pValue);
+
+ }else{
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "halui Bridge: Failed python function");
+ }
+ Py_DECREF(pFuncWrite);
+}
+
+static void py_call_request_MDI( int index)
+{
+ pFuncWrite = PyObject_GetAttrString(pInstance, "runIndexedMacro");
+
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallFunction(pFuncWrite, "i", index);
+ if (pValue == NULL){
+ fprintf(stderr, "halui bridge: runIndexedMacro function failed: returned NULL\n");
+ if (PyErr_Occurred()) PyErr_Print();
+ }
+ Py_DECREF(pValue);
+
+ }else{
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "halui Bridge: Failed python function runIndexedMacro");
+ }
+ Py_DECREF(pFuncWrite);
+}
+
+static void py_call_request_softkey( int index)
+{
+ pFuncWrite = PyObject_GetAttrString(pInstance, "softkey");
+
+ if (pFuncWrite && PyCallable_Check(pFuncWrite)) {
+ pValue = PyObject_CallFunction(pFuncWrite, "i", index);
+ if (pValue == NULL){
+ fprintf(stderr, "halui bridge: softkey function failed: returned NULL\n");
+ if (PyErr_Occurred()) PyErr_Print();
+ }
+ Py_DECREF(pValue);
+
+ }else{
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "halui Bridge: Failed python function softkey");
+ }
+ Py_DECREF(pFuncWrite);
+}
+static int py_call_get_axis_type( int index) {
+ int value = 0;
+ // check socket messages for selected axis
+ pFuncRead = PyObject_GetAttrString(pInstance, "getAxisIndexType");
+ if (pFuncRead && PyCallable_Check(pFuncRead)) {
+ pValue = PyObject_CallFunction(pFuncRead, "i", index);
+ if (pValue == NULL){
+ if (PyErr_Occurred()) PyErr_Print();
+ fprintf(stderr, "Halui Bridge: getSelectAxis function failed: returned NULL\n");
+ value = -1;
+ }else{
+ if (PyLong_Check(pValue)) {
+ value = (int) PyLong_AsLong(pValue);
+ //fprintf(stderr, "axis value %d\n",value);
+ if (PyErr_Occurred()) {
+ value = -1;
+ // Handle conversion error
+ PyErr_Print();
+ // Clear the error state if needed
+ PyErr_Clear();
+ }
+ }
+ }
+ Py_DECREF(pValue);
+ }
+ Py_DECREF(pFuncRead);
+ return value;
+}
// this function looks if any of the hal pins has changed
// and sends appropriate messages if so
@@ -1611,10 +2034,55 @@ static void check_hal_changes()
hal_bit_t bit;
int js;
hal_float_t floatt;
+ double jogspeed;
+ double angularjogspeed;
int jjog_speed_changed;
int ajog_speed_changed;
+ int is_any_axis_selected, deselected;
+
+ // get python to process socket messages
+ pFuncRead = PyObject_GetAttrString(pInstance, "readMsg");
+
+ if (pFuncRead && PyCallable_Check(pFuncRead)) {
+ pValue = PyObject_CallNoArgs(pFuncRead);
+ if (pValue == NULL){
+ fprintf(stderr, "Halui Bridge: readMsg function failed: returned NULL\n");
+ }
+ if (PyErr_Occurred()) PyErr_Print();
+ }else{
+ if (PyErr_Occurred()){
+ PyErr_Print();
+ }
+ fprintf(stderr, "Bridge: Failed python function");
+ exit(1);
+ }
+ Py_DECREF(pFuncRead);
+ Py_DECREF(pValue);
+
+ // check socket messages for current axis selection
+ int value = py_call_get_axis_selected();
local_halui_str new_halui_data_mutable;
+
+ if (value != lastaxis) {
+ // inject socket axis selection over hal data
+ for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) {
+ if ( !(axis_mask & (1 << axis_num)) ) { continue; }
+
+ if (axis_num == value) {
+ *(halui_data->axis_nr_select[axis_num]) = 1;
+ }else{
+ *(halui_data->axis_nr_select[axis_num]) = 0;
+ }
+ }
+ if (value == 100) {
+ *(halui_data->mpg_select0) = 1;
+ }else{
+ *(halui_data->mpg_select0) = 0;
+ }
+ lastaxis = value;
+ }
+
copy_hal_data(*halui_data, new_halui_data_mutable);
const local_halui_str &new_halui_data = new_halui_data_mutable;
@@ -1659,6 +2127,16 @@ static void check_hal_changes()
if (check_bit_changed(new_halui_data.flood_off, old_halui_data.flood_off) != 0)
sendFloodOff();
+ if (check_bit_changed(new_halui_data.cycle_start, old_halui_data.cycle_start) != 0){
+ fprintf(stderr, "cycle-start value = %i\n", new_halui_data.cycle_start );
+ py_call_cycleStart();
+ }
+
+ if (check_bit_changed(new_halui_data.cycle_pause, old_halui_data.cycle_pause) != 0){
+ fprintf(stderr, "cycle-pause value = %i\n", new_halui_data.cycle_pause );
+ py_call_cyclePause();
+ }
+
if (check_bit_changed(new_halui_data.program_run, old_halui_data.program_run) != 0)
sendProgramRun(0);
@@ -1826,6 +2304,7 @@ static void check_hal_changes()
} else {
jjog_speed_changed = 0;
}
+
// axis stuff (selection, homing..)
aselect_changed = -1; // flag to see if the selected joint changed
@@ -1833,11 +2312,43 @@ static void check_hal_changes()
// re-start the jog with the new speed
if (fabs(old_halui_data.ajog_speed - new_halui_data.ajog_speed) > 0.00001) {
old_halui_data.ajog_speed = new_halui_data.ajog_speed;
+ internaljogspeed = new_halui_data.ajog_speed;
+ ajog_speed_changed = 1;
+ py_call_axis_jogspeed(internaljogspeed);
+ } else {
+ ajog_speed_changed = 0;
+ }
+
+ // check socket messages for jogspeed
+ jogspeed = py_call_axis_get_jogspeed();
+ if (fabs(jogspeed - lastjogspeed) > 0.00001) {
+ ajog_speed_changed = 1;
+ lastjogspeed = jogspeed;
+ internaljogspeed = jogspeed;
+ fprintf(stderr, "JogRate value = %f\n", jogspeed );
+ }
+
+ // if the ANGULAR jog-speed changes while in a continuous jog, we want to
+ // re-start the jog with the new speed
+ if (fabs(old_halui_data.ajog_speed_angular - new_halui_data.ajog_speed_angular) > 0.00001) {
+ old_halui_data.ajog_speed_angular = new_halui_data.ajog_speed_angular;
+ internalangularjogspeed = new_halui_data.ajog_speed_angular;
ajog_speed_changed = 1;
+ py_call_axis_angular_jogspeed(internalangularjogspeed);
} else {
ajog_speed_changed = 0;
}
+ // check socket messages for ANGULAR jogspeed
+ angularjogspeed = py_call_axis_get_angular_jogspeed();
+ if (fabs(angularjogspeed - lastangularjogspeed) > 0.00001) {
+ ajog_speed_changed = 1;
+ lastangularjogspeed = angularjogspeed;
+ internalangularjogspeed = angularjogspeed;
+ fprintf(stderr, "angular JogRate value = %f\n", angularjogspeed );
+ }
+
+
for (joint=0; joint < num_joints; joint++) {
if (check_bit_changed(new_halui_data.joint_home[joint], old_halui_data.joint_home[joint]) != 0)
sendHome(joint);
@@ -1917,47 +2428,65 @@ static void check_hal_changes()
}
}
+
+ // check thru axes
+ is_any_axis_selected = 0;
+ deselected = 0;
for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) {
+
if ( !(axis_mask & (1 << axis_num)) ) { continue; }
+
+ // check for axis type: linear/rotary
+ if (py_call_get_axis_type(axis_num) == 1){
+ tempjogspeed = internaljogspeed;
+ }else{
+ tempjogspeed = internalangularjogspeed;
+ }
+
+ // axis jog -
bit = new_halui_data.ajog_minus[axis_num];
if ((bit != old_halui_data.ajog_minus[axis_num]) || (bit && ajog_speed_changed)) {
if (bit != 0)
- sendJogCont(axis_num,-new_halui_data.ajog_speed,JOGTELEOP);
+ sendJogCont(axis_num,-tempjogspeed,JOGTELEOP);
else
sendJogStop(axis_num,JOGTELEOP);
old_halui_data.ajog_minus[axis_num] = bit;
}
+ // axis jog +
bit = new_halui_data.ajog_plus[axis_num];
if ((bit != old_halui_data.ajog_plus[axis_num]) || (bit && ajog_speed_changed)) {
if (bit != 0)
- sendJogCont(axis_num,new_halui_data.ajog_speed,JOGTELEOP);
+ sendJogCont(axis_num,tempjogspeed,JOGTELEOP);
else
sendJogStop(axis_num,JOGTELEOP);
old_halui_data.ajog_plus[axis_num] = bit;
}
+ // axis jog analog
floatt = new_halui_data.ajog_analog[axis_num];
bit = (fabs(floatt) > new_halui_data.ajog_deadband);
if ((floatt != old_halui_data.ajog_analog[axis_num]) || (bit && ajog_speed_changed)) {
if (bit)
- sendJogCont(axis_num,(new_halui_data.ajog_speed) * (new_halui_data.ajog_analog[axis_num]),JOGTELEOP);
+ sendJogCont(axis_num,(tempjogspeed) * (new_halui_data.ajog_analog[axis_num]),JOGTELEOP);
else
sendJogStop(axis_num,JOGTELEOP);
old_halui_data.ajog_analog[axis_num] = floatt;
}
+ // axis jog + increment
bit = new_halui_data.ajog_increment_plus[axis_num];
if (bit != old_halui_data.ajog_increment_plus[axis_num]) {
if (bit)
- sendJogIncr(axis_num, new_halui_data.ajog_speed, new_halui_data.ajog_increment[axis_num],JOGTELEOP);
+ sendJogIncr(axis_num, tempjogspeed, new_halui_data.ajog_increment[axis_num],JOGTELEOP);
old_halui_data.ajog_increment_plus[axis_num] = bit;
}
+ // jog a- increment
bit = new_halui_data.ajog_increment_minus[axis_num];
if (bit != old_halui_data.ajog_increment_minus[axis_num]) {
if (bit)
- sendJogIncr(axis_num, new_halui_data.ajog_speed, -(new_halui_data.ajog_increment[axis_num]),JOGTELEOP);
+ sendJogIncr(axis_num, tempjogspeed, -(new_halui_data.ajog_increment[axis_num]),JOGTELEOP);
old_halui_data.ajog_increment_minus[axis_num] = bit;
}
@@ -1965,30 +2494,64 @@ static void check_hal_changes()
bit = new_halui_data.axis_nr_select[axis_num];
if (bit != old_halui_data.axis_nr_select[axis_num]) {
if (bit != 0) {
- *halui_data->axis_selected = axis_num;
- aselect_changed = axis_num; // flag that we changed the selected axis
- }
- old_halui_data.axis_nr_select[axis_num] = bit;
- }
+ is_any_axis_selected = 1;
+ *halui_data->axis_selected = axis_num;
+ py_call_axis_changed(axis_num);
+ aselect_changed = axis_num; // flag that we changed the selected axis
+ }else{
+ deselected = 1;
+ }
+ old_halui_data.axis_nr_select[axis_num] = bit;
+ }
+ }
+
+ // is MPG0 selected?
+ bit = new_halui_data.mpg_select0;
+ if (bit != old_halui_data.mpg_select0) {
+ if (bit != 0) {
+ is_any_axis_selected = 1;
+ py_call_axis_changed(100);
+ *halui_data->axis_selected = 100;
+ }else{
+ deselected = 1;
+ }
+ old_halui_data.mpg_select0 = bit;
+ }
+
+
+ // last axis has been deselected - no axis is selected now
+ if (is_any_axis_selected == 0 and deselected == 1) {
+ py_call_axis_changed(-1);
+ *halui_data->axis_selected = -1;
}
if (aselect_changed >= 0) {
- for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) {
- if ( !(axis_mask & (1 << axis_num)) ) { continue; }
- if (axis_num != aselect_changed) {
- *(halui_data->axis_is_selected[axis_num]) = 0;
+ fprintf(stderr, "halui Bridge: axis selected %d\n",aselect_changed);
+ for (axis_num = 0; axis_num < EMCMOT_MAX_AXIS; axis_num++) {
+ if ( !(axis_mask & (1 << axis_num)) ) { continue; }
+ if (axis_num != aselect_changed) {
+ *(halui_data->axis_is_selected[axis_num]) = 0;
if (jogging_selected_axis(old_halui_data) && !jogging_axis(old_halui_data, axis_num)) {
- sendJogStop(axis_num,JOGTELEOP);
+ sendJogStop(axis_num,JOGTELEOP);
}
} else {
- *(halui_data->axis_is_selected[axis_num]) = 1;
+
+ // check for axis type: linear/rotary
+ if (py_call_get_axis_type(axis_num) == 1){
+ tempjogspeed = internaljogspeed;
+ }else{
+ tempjogspeed = internalangularjogspeed;
+ }
+
+ *(halui_data->axis_is_selected[axis_num]) = 1;
if (*halui_data->ajog_plus[num_axes]) {
- sendJogCont(axis_num, new_halui_data.ajog_speed,JOGTELEOP);
+ fprintf(stderr, "halui: jog plus: %d\n",num_axes);
+ sendJogCont(axis_num, tempjogspeed,JOGTELEOP);
} else if (*halui_data->ajog_minus[num_axes]) {
- sendJogCont(axis_num, -new_halui_data.ajog_speed,JOGTELEOP);
+ sendJogCont(axis_num, -tempjogspeed,JOGTELEOP);
}
- }
- }
+ }
+ }
}
if (check_bit_changed(new_halui_data.joint_home[num_joints], old_halui_data.joint_home[num_joints]) != 0)
@@ -2036,8 +2599,17 @@ static void check_hal_changes()
bit = new_halui_data.ajog_minus[EMCMOT_MAX_AXIS];
js = new_halui_data.axis_selected;
if ((bit != old_halui_data.ajog_minus[EMCMOT_MAX_AXIS]) || (bit && ajog_speed_changed)) {
- if (bit != 0)
- sendJogCont(js, -new_halui_data.ajog_speed,JOGTELEOP);
+ if (bit != 0){
+
+ // check for axis type: linear/rotary
+ if (py_call_get_axis_type(js) == 1){
+ tempjogspeed = internaljogspeed;
+ }else{
+ tempjogspeed = internalangularjogspeed;
+ }
+
+ sendJogCont(js, -tempjogspeed,JOGTELEOP);
+ }
else
sendJogStop(js,JOGTELEOP);
old_halui_data.ajog_minus[EMCMOT_MAX_AXIS] = bit;
@@ -2046,8 +2618,17 @@ static void check_hal_changes()
bit = new_halui_data.ajog_plus[EMCMOT_MAX_AXIS];
js = new_halui_data.axis_selected;
if ((bit != old_halui_data.ajog_plus[EMCMOT_MAX_AXIS]) || (bit && ajog_speed_changed)) {
- if (bit != 0)
- sendJogCont(js,new_halui_data.ajog_speed,JOGTELEOP);
+ if (bit != 0){
+
+ // check for axis type: linear/rotary
+ if (py_call_get_axis_type(js) == 1){
+ tempjogspeed = internaljogspeed;
+ }else{
+ tempjogspeed = internalangularjogspeed;
+ }
+
+ sendJogCont(js,tempjogspeed,JOGTELEOP);
+ }
else
sendJogStop(js,JOGTELEOP);
old_halui_data.ajog_plus[EMCMOT_MAX_AXIS] = bit;
@@ -2057,7 +2638,7 @@ static void check_hal_changes()
js = new_halui_data.axis_selected;
if (bit != old_halui_data.ajog_increment_plus[EMCMOT_MAX_AXIS]) {
if (bit)
- sendJogIncr(js, new_halui_data.ajog_speed, new_halui_data.ajog_increment[EMCMOT_MAX_AXIS],JOGTELEOP);
+ sendJogIncr(js, tempjogspeed, new_halui_data.ajog_increment[EMCMOT_MAX_AXIS],JOGTELEOP);
old_halui_data.ajog_increment_plus[EMCMOT_MAX_AXIS] = bit;
}
@@ -2065,14 +2646,52 @@ static void check_hal_changes()
js = new_halui_data.axis_selected;
if (bit != old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS]) {
if (bit)
- sendJogIncr(js, new_halui_data.ajog_speed, -(new_halui_data.ajog_increment[EMCMOT_MAX_AXIS]),JOGTELEOP);
+ sendJogIncr(js, tempjogspeed, -(new_halui_data.ajog_increment[EMCMOT_MAX_AXIS]),JOGTELEOP);
old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS] = bit;
}
+ // run HALUI commands
for(int n = 0; n < num_mdi_commands; n++) {
if (check_bit_changed(new_halui_data.mdi_commands[n], old_halui_data.mdi_commands[n]) != 0)
sendMdiCommand(n);
}
+
+ // request GUI to run MDI commands
+ for(int n = 0; n < num_gui_mdi_commands; n++) {
+ if (check_bit_changed(new_halui_data.gui_mdi_commands[n], old_halui_data.gui_mdi_commands[n]) != 0){
+ fprintf(stderr,"GUI MDI command called index: %i\n", n);
+ py_call_request_MDI(n);
+ }
+ }
+
+ // request GUI soft keys
+ for(int n = 0; n < num_gui_soft_keys; n++) {
+ if (check_bit_changed(new_halui_data.gui_soft_keys[n], old_halui_data.gui_soft_keys[n]) != 0){
+ fprintf(stderr,"GUI SOFTKEY called index: %i\n", n);
+ py_call_request_softkey(n);
+ }
+ }
+
+ if (check_bit_changed(new_halui_data.gui_ok, old_halui_data.gui_ok) != 0) {
+ fprintf(stderr,"GUI OK command called\n");
+ py_call_ok();
+ }
+
+ if (check_bit_changed(new_halui_data.gui_cancel, old_halui_data.gui_cancel) != 0) {
+ fprintf(stderr,"GUI CANCEL command called\n");
+ py_call_cancel();
+ }
+
+ if (check_bit_changed(new_halui_data.gui_reload, old_halui_data.gui_reload) != 0) {
+ fprintf(stderr,"GUI RELOAD DISPLAY command called\n");
+ py_call_reload_display();
+ }
+
+ if (check_bit_changed(new_halui_data.gui_shutdown, old_halui_data.gui_shutdown) != 0) {
+ fprintf(stderr,"GUI SHUTDOWN command called\n");
+ py_call_shutdown_controller();
+ }
+
}
// this function looks at the received NML status message
@@ -2295,6 +2914,27 @@ int main(int argc, char *argv[])
exit(1);
}
+ /* import the python module and get references for needed function */
+
+ PyConfig config;
+ PyConfig_InitPythonConfig(&config);
+ char name[] = "halui";
+ wchar_t *wname = Py_DecodeLocale(name, NULL);
+ PyConfig_SetString(&config, &config.program_name, wname);
+ Py_Initialize();
+
+ PyRun_SimpleString("print('PYTHON EMBEDDED!!')\n");
+ pModule = PyImport_ImportModule("bridgeui.bridge");
+ if (pModule != NULL) {
+ pClass = PyObject_GetAttrString(pModule, "Bridge");
+ pInstance = PyObject_CallObject(pClass, NULL);
+ }else{
+ PyErr_Print();
+ fprintf(stderr, "bridge: Failed to load \"%s\"\n", "pyui");
+ exit(1);
+ }
+ Py_DECREF(pClass);
+
// get configuration information
if (0 != iniLoad(emc_inifile)) {
rcs_print_error("iniLoad error\n");
@@ -2310,6 +2950,9 @@ int main(int argc, char *argv[])
//initialize safe values
hal_init_pins();
+
+
+
// init NML
if (0 != tryNml()) {
rcs_print_error("can't connect to emc\n");
@@ -2345,6 +2988,8 @@ int main(int argc, char *argv[])
}
}
check_hal_changes(); //if anything changed send NML messages
+
+
modify_hal_pins(); //if status changed modify HAL too
esleep(0.02); //sleep for a while
updateStatus();
diff --git a/src/hal/user_comps/hal_bridge.py b/src/hal/user_comps/hal_bridge.py
index 1833ab7e7af..39977946508 100644
--- a/src/hal/user_comps/hal_bridge.py
+++ b/src/hal/user_comps/hal_bridge.py
@@ -118,7 +118,10 @@ def init_hal(self):
def init_write(self):
context = zmq.Context()
self.writeSocket = context.socket(zmq.PUB)
- self.writeSocket.bind(self.writeAddress)
+ try:
+ self.writeSocket.bind(self.writeAddress)
+ except zmq.error.ZMQError:
+ LOG.critical(f'Write address already in use? {self.writeAddress}')
def init_read(self):
# ZeroMQ Context