From 95c5d385cb75c22833c9b7c07ac4f0c5b4247072 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 16 Aug 2025 11:21:10 -0700 Subject: [PATCH 01/32] add bridgeui folder for HALUI --- lib/python/bridgeui/__init__.py | 3 + lib/python/bridgeui/bridge.py | 302 ++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 lib/python/bridgeui/__init__.py create mode 100644 lib/python/bridgeui/bridge.py 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..54ea67d225e --- /dev/null +++ b/lib/python/bridgeui/bridge.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 + +import os +import sys +import time + +import json +import signal + +import hal +from qtvcp.qt_halobjects import Qhal +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} + 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_hal() + if ZMQ: + self.init_read() + self.init_write() + + def update(self, *arg): + print(self, arg) + raw=arg[0]; row=arg[1];column=arg[2];state=arg[3] + LOG.debug('raw {}, row {}, col {}, state {}'.format(raw,row,column,state)) + print ('raw',raw,'row:',row,'column:',column,'state:',state) + self.writeMsg('set_selected_axis','Y') + self.activeJoint.set(10) + + def init_hal(self): + self.comp = h = hal.component("bridge") + QHAL = Qhal(comp=self.comp, hal=hal) + + self.jogRate = QHAL.newpin("jog-rate", hal.HAL_FLOAT, hal.HAL_OUT) + self.jogRateIn = QHAL.newpin("jog-rate-in", hal.HAL_FLOAT, hal.HAL_IN) + self.jogRateIn.pinValueChanged.connect(self.pinChanged) + + self.jogRateAngular = QHAL.newpin("jog-rate-angular", hal.HAL_FLOAT, hal.HAL_OUT) + self.jogRateAngularIn = QHAL.newpin("jog-rate-angular-in", hal.HAL_FLOAT, hal.HAL_OUT) + self.jogRateAngularIn.pinValueChanged.connect(self.pinChanged) + + self.jogIncrement = QHAL.newpin("jog-increment", hal.HAL_FLOAT, hal.HAL_OUT) + self.jogIncrementAngular = QHAL.newpin("jog-increment-angular", hal.HAL_FLOAT, hal.HAL_OUT) + self.activeJoint = QHAL.newpin('joint-selected', hal.HAL_S32, hal.HAL_OUT) + + for i in (self.INFO.AVAILABLE_AXES): + let = i.lower() + # input + self['axis{}Select'.format(let)] = QHAL.newpin('axis-%s-select'%let, hal.HAL_BIT, hal.HAL_IN) + self['axis{}Select'.format(let)].pinValueChanged.connect(self.pinChanged) + # output + self['Axis{}IsSelected'.format(let)] = QHAL.newpin('axis-%s-is-selected'%let, hal.HAL_BIT, hal.HAL_OUT) + + self.cycle_start = QHAL.newpin('cycle-start-in',QHAL.HAL_BIT, QHAL.HAL_IN) + self.cycle_start.pinValueChanged.connect(self.pinChanged) + self.cycle_pause = QHAL.newpin('cycle-pause-in',QHAL.HAL_BIT, QHAL.HAL_IN) + self.cycle_pause.pinValueChanged.connect(self.pinChanged) + + for i in self.INFO.MDI_COMMAND_DICT: + LOG.debug('{} {}'.format(i,self.INFO.MDI_COMMAND_DICT.get(i))) + self[i] = QHAL.newpin('ini-mdi-cmd-{}'.format(i),QHAL.HAL_BIT, QHAL.HAL_IN) + self[i].pinValueChanged.connect(self.runMacroChanged) + + for i in self.INFO.INI_MACROS: + name = i.split()[0] + LOG.debug('{} {}'.format(name,i)) + self[name] = QHAL.newpin('ini-macro-cmd-{}'.format(name),QHAL.HAL_BIT, QHAL.HAL_IN) + self[name].pinValueChanged.connect(self.runMacroChanged) + + QHAL.setUpdateRate(100) + h.ready() + + 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, msg): + 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 output HAL pins from messages from hal_glib + def action(self, msg, data): + LOG.debug('{} {}'.format(msg, data)) + if msg == 'jograte-changed': + self.jogRate.set(float(data[0])) + elif msg == 'jograte-angular-changed': + self.jogRateAngular.set(float(data[0])) + elif msg == 'jogincrements-changed': + self.jogIncrement.set(float(data[0][0])) + elif msg == 'jogincrement-angular-changed': + self.jogIncremtAngular.set(float(data[0][0])) + elif msg == 'joint-selection-changed': + self.activeJoint.set(int(data[0])) + elif msg == 'axis-selection-changed': + flag = 1 + for i in(self.INFO.AVAILABLE_AXES): + if data[0] == i: + state = True + self.currentSelectedAxis = data[0] + flag = 0 + else: + state = False + self['Axis{}IsSelected'.format(i.lower())].set(state) + self.axesSelected[i] = int(state) + if flag: + self.currentSelectedAxis = 'None' + #print ('axis state', self.axesSelected) + + # 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'))]) + + # callback from HAL input pins + def pinChanged(self, pinObject, value): + LOG.debug('Pin name:{} changed value to {}'.format(pinObject.text(), value)) + #print(type(value)) + # Axis selction change request + if 'select' in pinObject.text(): + if bool(value) == False: + pass + #print('Not true state') + return + for i in (self.INFO.AVAILABLE_AXES): + if '-{}-'.format(i.lower()) in pinObject.text(): + self.writeMsg('set_selected_axis', i) + break + else: + if 'None' in pinObject.text(): + self.writeMsg('set_selected_axis', '') + + # cycle start + elif self.cycle_start == pinObject: + if value: + self.writeMsg('request_cycle_start', value) + + # cycle pause + elif self.cycle_pause == pinObject: + #if value: + self.writeMsg('request_cycle_pause', value) + + # linear jog rate + elif self.jogRateIn == pinObject: + self.writeMsg('set_jograte', value) + + # angular jog rate + elif self.jogRateAngularIn == pinObject: + self.writeMsg('set_jograte_angular', value) + + # catch all default + else: + self.writeMsg(pinObject.text(),value) + + # callback; request to run a specific macro + def runMacroChanged(self, pinObject, value): + LOG.debug('Macro Pin name:{} changed value to {}'.format(pinObject.text(), value)) + #LOG.debug(type(value)) + name = pinObject.text().strip('macro-cmd-') + if value: + self.writeMsg('request_macro_call', name) + + def shutdown(self,signum=None,stack_frame=None): + LOG.debug('shutdown') + global app + app.quit() + + def getJogRate(self): + return self.jogRate.get() + def setJogRate(self, value): + self.writeMsg('set_jograte', value) + + def getJogRateAngular(self): + return self.jogRateAngular.get() + def setJogRateAngular(self, value): + self.writeMsg('set_jograte_angular', value) + + def getSelectedAxis(self): + name = self.currentSelectedAxis + if name == 'None': + index = -1 + else: + index = 'XYZABCUVW'.index(name) + return index + def setSelectedAxis(self, value): + if value < 0: + letter = 'None' + else: + letter ='XYZABCUVW'[value] + self.writeMsg('set_selected_axis', letter) + + def isAxisSelected(self, index): + letter = 'XYZABCUVW'[index] + return int(self.axesSelected[letter]) + + 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_()) From f7c752a47553b0126b78cc735305f73a2b5b82d9 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 2 Aug 2025 19:38:15 -0700 Subject: [PATCH 02/32] halui initial testing of embedded python halui --- .../qtdragon/qtdragon_xyz/qtdragon_metric.ini | 2 +- src/emc/usr_intf/Submakefile | 3 +- src/emc/usr_intf/halui.cc | 244 ++++++++++++++++-- 3 files changed, 226 insertions(+), 23 deletions(-) diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini index 327d051bd5b..a00b52ab427 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/src/emc/usr_intf/Submakefile b/src/emc/usr_intf/Submakefile index 0b1df44be40..2efe36b90a9 100644 --- a/src/emc/usr_intf/Submakefile +++ b/src/emc/usr_intf/Submakefile @@ -37,5 +37,6 @@ TARGETS += ../bin/linuxcnclcd ../bin/halui: $(call TOOBJS, $(HALUISRCS)) ../lib/liblinuxcnc.a ../lib/liblinuxcncini.so.0 ../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/halui.cc b/src/emc/usr_intf/halui.cc index 269debe2790..527c763dc77 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -39,6 +39,10 @@ #include #include "tooldata.hh" +#define PY_SSIZE_T_CLEAN +#include +#include + /* Using halui: see the man page */ static int axis_mask = 0; @@ -230,9 +234,16 @@ 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 int lastaxis = -1; + static char *mdi_commands[MDI_MAX]; static int num_mdi_commands=0; static int have_home_all = 0; @@ -1673,7 +1684,101 @@ static bool jogging_selected_axis(local_halui_str &hal) { return (hal.ajog_plus[EMCMOT_MAX_AXIS] || hal.ajog_minus[EMCMOT_MAX_AXIS]); } +static double write_msg_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 write_msg_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(); + }else{ + Py_DECREF(pValue); + } + + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function"); + } + Py_DECREF(pFuncWrite); +} + +static int write_msg_get_axis_selected() { + int value = 0; + // check socket messages for jogspeed + 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 write_msg_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(); + }else{ + Py_DECREF(pValue); + } + }else{ + if (PyErr_Occurred()) PyErr_Print(); + fprintf(stderr, "halui Bridge: Failed python function"); + } + Py_DECREF(pFuncWrite); +} // this function looks if any of the hal pins has changed // and sends appropriate messages if so static void check_hal_changes() @@ -1684,13 +1789,30 @@ static void check_hal_changes() hal_bit_t bit; int js; hal_float_t floatt; + double jogspeed; int jjog_speed_changed; int ajog_speed_changed; + int is_any_axis_selected, deselected; local_halui_str new_halui_data_mutable; copy_hal_data(*halui_data, new_halui_data_mutable); const local_halui_str &new_halui_data = new_halui_data_mutable; + // read socket messages + pFuncRead = PyObject_GetAttrString(pInstance, "readMsg"); + if (pFuncRead && PyCallable_Check(pFuncRead)) { + pValue = PyObject_CallFunction(pFuncRead, "O", pClass); + 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); + } //check if machine_on pin has changed (the rest work exactly the same) if (check_bit_changed(new_halui_data.machine_on, old_halui_data.machine_on) != 0) @@ -1906,11 +2028,23 @@ 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; + write_msg_axis_jogspeed(internaljogspeed); } else { ajog_speed_changed = 0; } + // check socket messages for jogspeed + jogspeed = write_msg_axis_get_jogspeed(); + if (fabs(jogspeed - lastjogspeed) > 0.00001) { + ajog_speed_changed = 1; + lastjogspeed = jogspeed; + internaljogspeed = jogspeed; + fprintf(stderr, "JogRate value = %f\n", jogspeed ); + } + + 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); @@ -1990,47 +2124,57 @@ 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; } + + // 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,-internaljogspeed,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,internaljogspeed,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,(internaljogspeed) * (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, internaljogspeed, 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, internaljogspeed, -(new_halui_data.ajog_increment[axis_num]),JOGTELEOP); old_halui_data.ajog_increment_minus[axis_num] = bit; } @@ -2038,30 +2182,46 @@ 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) { + is_any_axis_selected = 1; *halui_data->axis_selected = axis_num; + write_msg_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; - } + } + } + // last axis has been deselected - no axis is selected now + if (is_any_axis_selected == 0 and deselected == 1) { + write_msg_axis_changed(-1); + } + + // check socket messages for axis selection change + int value = write_msg_get_axis_selected(); + if (value != lastaxis){ + aselect_changed = lastaxis = value; } 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; + *(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, internaljogspeed,JOGTELEOP); } else if (*halui_data->ajog_minus[num_axes]) { - sendJogCont(axis_num, -new_halui_data.ajog_speed,JOGTELEOP); + sendJogCont(axis_num, -internaljogspeed,JOGTELEOP); } - } - } + } + } } if (check_bit_changed(new_halui_data.joint_home[num_joints], old_halui_data.joint_home[num_joints]) != 0) @@ -2110,7 +2270,7 @@ static void check_hal_changes() 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); + sendJogCont(js, -internaljogspeed,JOGTELEOP); else sendJogStop(js,JOGTELEOP); old_halui_data.ajog_minus[EMCMOT_MAX_AXIS] = bit; @@ -2120,7 +2280,7 @@ static void check_hal_changes() 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); + sendJogCont(js,internaljogspeed,JOGTELEOP); else sendJogStop(js,JOGTELEOP); old_halui_data.ajog_plus[EMCMOT_MAX_AXIS] = bit; @@ -2130,7 +2290,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, internaljogspeed, new_halui_data.ajog_increment[EMCMOT_MAX_AXIS],JOGTELEOP); old_halui_data.ajog_increment_plus[EMCMOT_MAX_AXIS] = bit; } @@ -2138,7 +2298,7 @@ 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, internaljogspeed, -(new_halui_data.ajog_increment[EMCMOT_MAX_AXIS]),JOGTELEOP); old_halui_data.ajog_increment_minus[EMCMOT_MAX_AXIS] = bit; } @@ -2383,6 +2543,46 @@ int main(int argc, char *argv[]) //initialize safe values hal_init_pins(); + + /* 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); + pFuncWrite = PyObject_GetAttrString(pInstance, "update"); + + if (pFuncWrite && PyCallable_Check(pFuncWrite)) { + pValue = PyObject_CallFunction(pFuncWrite, "Olllh", pClass, 0, 0, 0, 0); + if (pValue == NULL){ + fprintf(stderr, "Panelui: update 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(pValue); + } + Py_DECREF(pFuncWrite); + }else{ + PyErr_Print(); + fprintf(stderr, "bridge: Failed to load \"%s\"\n", "pyui"); + exit(1); + } + + // init NML if (0 != tryNml()) { rcs_print_error("can't connect to emc\n"); @@ -2418,6 +2618,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(); From 1e32c84d6a3924e540d1901e384ef875ff5952f0 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 16 Aug 2025 10:13:59 -0700 Subject: [PATCH 03/32] halui -inject socket axis selection --- src/emc/usr_intf/halui.cc | 53 ++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 527c763dc77..a8b930b1ff3 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -1279,6 +1279,9 @@ static void sendJogStop(int ja, int jjogmode) static void sendJogCont(int ja, double speed, int jjogmode) { + // no selected axis/joint + if (ja < 0) { return; } + EMC_JOG_CONT emc_jog_cont_msg; if (emcStatus->task.state != EMC_TASK_STATE::ON) { return; } @@ -1629,7 +1632,7 @@ static void hal_init_pins() *(halui_data->ajog_speed) = 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 @@ -1794,11 +1797,7 @@ static void check_hal_changes() int ajog_speed_changed; int is_any_axis_selected, deselected; - local_halui_str new_halui_data_mutable; - copy_hal_data(*halui_data, new_halui_data_mutable); - const local_halui_str &new_halui_data = new_halui_data_mutable; - - // read socket messages + // get python to process socket messages pFuncRead = PyObject_GetAttrString(pInstance, "readMsg"); if (pFuncRead && PyCallable_Check(pFuncRead)) { pValue = PyObject_CallFunction(pFuncRead, "O", pClass); @@ -1814,6 +1813,29 @@ static void check_hal_changes() exit(1); } + // check socket messages for current axis selection + int value = write_msg_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; + } + } + lastaxis = value; + } + + copy_hal_data(*halui_data, new_halui_data_mutable); + const local_halui_str &new_halui_data = new_halui_data_mutable; + + //check if machine_on pin has changed (the rest work exactly the same) if (check_bit_changed(new_halui_data.machine_on, old_halui_data.machine_on) != 0) sendMachineOn(); //send MachineOn NML command @@ -2182,25 +2204,20 @@ 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) { - is_any_axis_selected = 1; - *halui_data->axis_selected = axis_num; - write_msg_axis_changed(axis_num); - aselect_changed = axis_num; // flag that we changed the selected axis + is_any_axis_selected = 1; + *halui_data->axis_selected = axis_num; + write_msg_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; } + old_halui_data.axis_nr_select[axis_num] = bit; + } } // last axis has been deselected - no axis is selected now if (is_any_axis_selected == 0 and deselected == 1) { write_msg_axis_changed(-1); - } - - // check socket messages for axis selection change - int value = write_msg_get_axis_selected(); - if (value != lastaxis){ - aselect_changed = lastaxis = value; + *halui_data->axis_selected = -1; } if (aselect_changed >= 0) { From 89673bcb80207cb0d5e4b568d6738e20afbfb360 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 16 Aug 2025 11:12:19 -0700 Subject: [PATCH 04/32] gladevcp -speedcontrol: put Gstat status in the loop Gstat sends out socket messages for jog rate --- lib/python/gladevcp/speedcontrol.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/python/gladevcp/speedcontrol.py b/lib/python/gladevcp/speedcontrol.py index 332f025ab0d..5dda24b65a3 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, 0, -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,7 @@ 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.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 +183,10 @@ 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('->>',self.type_linear_jog) + self._status.connect('jograte-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 +281,9 @@ 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) + if value != self._value: self._value = value self.set_value(self._value) @@ -453,6 +468,10 @@ 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._draw_widget() else: raise AttributeError('unknown property %s' % property.name) From e9566bbf5a33568f1ac8b0408d031a379e9a17e6 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 16 Aug 2025 23:30:26 -0700 Subject: [PATCH 05/32] gmoccapy -set property to speed control widget --- src/emc/usr_intf/gmoccapy/gmoccapy.glade | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.glade b/src/emc/usr_intf/gmoccapy/gmoccapy.glade index 9b5c2ee18df..1d720f12588 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.glade +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.glade @@ -1,5 +1,5 @@ - + @@ -236,14 +236,14 @@ False go-up - + True False - edit-undo - + True False + edit-undo True @@ -1339,6 +1339,7 @@ False rgb(255,129,22) 10500 + 0 mm/min 1500 From 7f8ac5390e88b1867f482f20d6fed49f028373fb Mon Sep 17 00:00:00 2001 From: CMorley Date: Tue, 19 Aug 2025 20:18:49 -0700 Subject: [PATCH 06/32] bridge -remove HAL pin code, get MDI commands working --- lib/python/bridgeui/bridge.py | 98 ++++++++++++++--------------------- 1 file changed, 38 insertions(+), 60 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index 54ea67d225e..32cc74b8440 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -8,7 +8,6 @@ import signal import hal -from qtvcp.qt_halobjects import Qhal from common.iniinfo import _IStat as IStatParent from common import logger @@ -59,7 +58,7 @@ def __init__(self, readAddress = "tcp://127.0.0.1:5690", signal.signal(signal.SIGTERM, self.shutdown) signal.signal(signal.SIGINT, self.shutdown) - self.init_hal() + self.init() if ZMQ: self.init_read() self.init_write() @@ -72,48 +71,14 @@ def update(self, *arg): self.writeMsg('set_selected_axis','Y') self.activeJoint.set(10) - def init_hal(self): - self.comp = h = hal.component("bridge") - QHAL = Qhal(comp=self.comp, hal=hal) + def init(self): + self.jogRate = 0 + self.jogRateAngular = 0 - self.jogRate = QHAL.newpin("jog-rate", hal.HAL_FLOAT, hal.HAL_OUT) - self.jogRateIn = QHAL.newpin("jog-rate-in", hal.HAL_FLOAT, hal.HAL_IN) - self.jogRateIn.pinValueChanged.connect(self.pinChanged) + self.jogIncrement = 0 + self.jogIncrementAngular = 0 - self.jogRateAngular = QHAL.newpin("jog-rate-angular", hal.HAL_FLOAT, hal.HAL_OUT) - self.jogRateAngularIn = QHAL.newpin("jog-rate-angular-in", hal.HAL_FLOAT, hal.HAL_OUT) - self.jogRateAngularIn.pinValueChanged.connect(self.pinChanged) - - self.jogIncrement = QHAL.newpin("jog-increment", hal.HAL_FLOAT, hal.HAL_OUT) - self.jogIncrementAngular = QHAL.newpin("jog-increment-angular", hal.HAL_FLOAT, hal.HAL_OUT) - self.activeJoint = QHAL.newpin('joint-selected', hal.HAL_S32, hal.HAL_OUT) - - for i in (self.INFO.AVAILABLE_AXES): - let = i.lower() - # input - self['axis{}Select'.format(let)] = QHAL.newpin('axis-%s-select'%let, hal.HAL_BIT, hal.HAL_IN) - self['axis{}Select'.format(let)].pinValueChanged.connect(self.pinChanged) - # output - self['Axis{}IsSelected'.format(let)] = QHAL.newpin('axis-%s-is-selected'%let, hal.HAL_BIT, hal.HAL_OUT) - - self.cycle_start = QHAL.newpin('cycle-start-in',QHAL.HAL_BIT, QHAL.HAL_IN) - self.cycle_start.pinValueChanged.connect(self.pinChanged) - self.cycle_pause = QHAL.newpin('cycle-pause-in',QHAL.HAL_BIT, QHAL.HAL_IN) - self.cycle_pause.pinValueChanged.connect(self.pinChanged) - - for i in self.INFO.MDI_COMMAND_DICT: - LOG.debug('{} {}'.format(i,self.INFO.MDI_COMMAND_DICT.get(i))) - self[i] = QHAL.newpin('ini-mdi-cmd-{}'.format(i),QHAL.HAL_BIT, QHAL.HAL_IN) - self[i].pinValueChanged.connect(self.runMacroChanged) - - for i in self.INFO.INI_MACROS: - name = i.split()[0] - LOG.debug('{} {}'.format(name,i)) - self[name] = QHAL.newpin('ini-macro-cmd-{}'.format(name),QHAL.HAL_BIT, QHAL.HAL_IN) - self[name].pinValueChanged.connect(self.runMacroChanged) - - QHAL.setUpdateRate(100) - h.ready() + self.activeJoint = 0 def init_write(self): context = zmq.Context() @@ -141,19 +106,19 @@ def readMsg(self, msg): y = json.loads(data) self. action(y.get('MESSAGE'),y.get('ARGS')) - # set our output HAL pins from messages from hal_glib + # set our variables from messages from hal_glib def action(self, msg, data): LOG.debug('{} {}'.format(msg, data)) if msg == 'jograte-changed': - self.jogRate.set(float(data[0])) + self.jogRate = float(data[0]) elif msg == 'jograte-angular-changed': - self.jogRateAngular.set(float(data[0])) + self.jogRateAngular = float(data[0]) elif msg == 'jogincrements-changed': - self.jogIncrement.set(float(data[0][0])) + self.jogIncrement = float(data[0][0]) elif msg == 'jogincrement-angular-changed': - self.jogIncremtAngular.set(float(data[0][0])) + self.jogIncremtAngular = float(data[0][0]) elif msg == 'joint-selection-changed': - self.activeJoint.set(int(data[0])) + self.activeJoint = int(data[0]) elif msg == 'axis-selection-changed': flag = 1 for i in(self.INFO.AVAILABLE_AXES): @@ -163,7 +128,6 @@ def action(self, msg, data): flag = 0 else: state = False - self['Axis{}IsSelected'.format(i.lower())].set(state) self.axesSelected[i] = int(state) if flag: self.currentSelectedAxis = 'None' @@ -183,8 +147,7 @@ def writeMsg(self, msg, data): # callback from HAL input pins def pinChanged(self, pinObject, value): LOG.debug('Pin name:{} changed value to {}'.format(pinObject.text(), value)) - #print(type(value)) - # Axis selction change request + # Axis selection change request if 'select' in pinObject.text(): if bool(value) == False: pass @@ -220,26 +183,41 @@ def pinChanged(self, pinObject, value): else: self.writeMsg(pinObject.text(),value) - # callback; request to run a specific macro - def runMacroChanged(self, pinObject, value): - LOG.debug('Macro Pin name:{} changed value to {}'.format(pinObject.text(), value)) - #LOG.debug(type(value)) - name = pinObject.text().strip('macro-cmd-') - if value: - self.writeMsg('request_macro_call', name) def shutdown(self,signum=None,stack_frame=None): LOG.debug('shutdown') global app app.quit() + def getMdiName(self, num): + if num >len(self.INFO.MDI_COMMAND_DICT)-1: + return 'None' + temp = list(self.INFO.MDI_COMMAND_DICT.keys())[num] + LOG.debug('{} {}'.format(num,temp)) + return temp + + def getMacroNames(self): + for i in self.INFO.INI_MACROS: + name = i.split()[0] + LOG.debug('{} {}'.format(name,i)) + + def runIndexedMacro(self, num): + name = self.getMdiName(num) + LOG.debug('Macro name:{} ,index: {}'.format(name, num)) + if name != 'None': + self.writeMsg('request_macro_call', name) + + def getMdiCount(self): + print(len(self.INFO.MDI_COMMAND_DICT)) + return len(self.INFO.MDI_COMMAND_DICT) + def getJogRate(self): - return self.jogRate.get() + return self.jogRate def setJogRate(self, value): self.writeMsg('set_jograte', value) def getJogRateAngular(self): - return self.jogRateAngular.get() + return self.jogRateAngular def setJogRateAngular(self, value): self.writeMsg('set_jograte_angular', value) From 315360e81204f8e671e0956687ba65cb72330617 Mon Sep 17 00:00:00 2001 From: CMorley Date: Tue, 19 Aug 2025 20:19:38 -0700 Subject: [PATCH 07/32] halui -code clenup, GUI based MDI command feature --- src/emc/usr_intf/halui.cc | 177 ++++++++++++++++++++++++++++---------- 1 file changed, 133 insertions(+), 44 deletions(-) diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index a8b930b1ff3..3b0febc2b3c 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -203,6 +203,7 @@ 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) \ \ FIELD(hal_float_t,units_per_mm) \ @@ -246,6 +247,9 @@ 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 comp_id, done; /* component ID, main while loop */ @@ -551,6 +555,65 @@ int halui_export_pin_OUT_bit(hal_bit_t **pin, const char *name) return 0; } +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 undex 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; +} /******************************************************************** * @@ -939,6 +1002,12 @@ int halui_hal_init(void) if (retval < 0) return retval; } + for (int n=0; ngui_mdi_commands[n]), comp_id, "halui.mdi-command-%s", py_call_get_mdi_name(n)); + if (retval < 0) return retval; + } + hal_ready(comp_id); return 0; } @@ -1573,6 +1642,13 @@ static int iniLoad(const char *filename) mdi_commands[num_mdi_commands++] = strdup(*mc); } + int temp = py_call_get_mdi_count(); + for (int n=0; n 0.00001) { ajog_speed_changed = 1; lastjogspeed = jogspeed; @@ -2206,7 +2303,7 @@ static void check_hal_changes() if (bit != 0) { is_any_axis_selected = 1; *halui_data->axis_selected = axis_num; - write_msg_axis_changed(axis_num); + py_call_axis_changed(axis_num); aselect_changed = axis_num; // flag that we changed the selected axis }else{ deselected = 1; @@ -2216,7 +2313,7 @@ static void check_hal_changes() } // last axis has been deselected - no axis is selected now if (is_any_axis_selected == 0 and deselected == 1) { - write_msg_axis_changed(-1); + py_call_axis_changed(-1); *halui_data->axis_selected = -1; } @@ -2319,10 +2416,19 @@ static void check_hal_changes() 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 ti 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){ + printf("GUI MDI command called index: %i\n", n); + py_call_request_MDI(n); + } + } } // this function looks at the received NML status message @@ -2545,22 +2651,6 @@ int main(int argc, char *argv[]) exit(1); } - // get configuration information - if (0 != iniLoad(emc_inifile)) { - rcs_print_error("iniLoad error\n"); - exit(2); - } - - //init HAL and export pins - if (0 != halui_hal_init()) { - rcs_print_error("hal_init error\n"); - exit(1); - } - - //initialize safe values - hal_init_pins(); - - /* import the python module and get references for needed function */ PyConfig config; @@ -2570,35 +2660,34 @@ int main(int argc, char *argv[]) PyConfig_SetString(&config, &config.program_name, wname); Py_Initialize(); - PyRun_SimpleString("print('PYTHON EMBEDDED!!')\n" - ); + PyRun_SimpleString("print('PYTHON EMBEDDED!!')\n"); pModule = PyImport_ImportModule("bridgeui.bridge"); if (pModule != NULL) { pClass = PyObject_GetAttrString(pModule, "Bridge"); pInstance = PyObject_CallObject(pClass, NULL); - pFuncWrite = PyObject_GetAttrString(pInstance, "update"); - - if (pFuncWrite && PyCallable_Check(pFuncWrite)) { - pValue = PyObject_CallFunction(pFuncWrite, "Olllh", pClass, 0, 0, 0, 0); - if (pValue == NULL){ - fprintf(stderr, "Panelui: update 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(pValue); - } - Py_DECREF(pFuncWrite); }else{ PyErr_Print(); fprintf(stderr, "bridge: Failed to load \"%s\"\n", "pyui"); exit(1); } + // get configuration information + if (0 != iniLoad(emc_inifile)) { + rcs_print_error("iniLoad error\n"); + exit(2); + } + + //init HAL and export pins + if (0 != halui_hal_init()) { + rcs_print_error("hal_init error\n"); + exit(1); + } + + //initialize safe values + hal_init_pins(); + + + // init NML if (0 != tryNml()) { From 777736953eb255c7a50767cfc69da51744bfa793 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 22 Aug 2025 15:32:30 -0700 Subject: [PATCH 08/32] halui -add cycle start cycle pause pins calls these functions in a GUI --- lib/python/bridgeui/bridge.py | 47 ++++++----------------------------- src/emc/usr_intf/halui.cc | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index 32cc74b8440..862ae0244d9 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -144,51 +144,18 @@ def writeMsg(self, msg, data): [bytes(topic.encode('utf-8')), bytes((message).encode('utf-8'))]) - # callback from HAL input pins - def pinChanged(self, pinObject, value): - LOG.debug('Pin name:{} changed value to {}'.format(pinObject.text(), value)) - # Axis selection change request - if 'select' in pinObject.text(): - if bool(value) == False: - pass - #print('Not true state') - return - for i in (self.INFO.AVAILABLE_AXES): - if '-{}-'.format(i.lower()) in pinObject.text(): - self.writeMsg('set_selected_axis', i) - break - else: - if 'None' in pinObject.text(): - self.writeMsg('set_selected_axis', '') - - # cycle start - elif self.cycle_start == pinObject: - if value: - self.writeMsg('request_cycle_start', value) - - # cycle pause - elif self.cycle_pause == pinObject: - #if value: - self.writeMsg('request_cycle_pause', value) - - # linear jog rate - elif self.jogRateIn == pinObject: - self.writeMsg('set_jograte', value) - - # angular jog rate - elif self.jogRateAngularIn == pinObject: - self.writeMsg('set_jograte_angular', value) - - # catch all default - else: - self.writeMsg(pinObject.text(),value) - - 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 getMdiName(self, num): if num >len(self.INFO.MDI_COMMAND_DICT)-1: return 'None' diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index 3b0febc2b3c..c23b55976d2 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -86,6 +86,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 */ \ @@ -555,6 +557,36 @@ 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 (pFuncRead && 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 (pFuncRead && 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 int py_call_get_mdi_count() { int value = 0; // check socket messages for jogspeed @@ -844,6 +876,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"); @@ -1973,6 +2009,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); From 53f56a9a5b28c7b68b443ea7a9c4dcb5ed243cdc Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 22 Aug 2025 15:33:19 -0700 Subject: [PATCH 09/32] qtdragon -adjust external pause message to toggle --- share/qtvcp/screens/qtdragon/qtdragon_handler.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index e911f8d54a5..58022147c4a 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -151,7 +151,7 @@ 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)) self.swoopPath = os.path.join(paths.IMAGEDIR,'lcnc_swoop.png') @@ -1252,6 +1252,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 From 2f799d9dbfb6ad1d075a60904f45dc296e8db7b5 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sun, 27 Jul 2025 14:05:58 -0700 Subject: [PATCH 10/32] gmoccapy/hal_bridge -allow hal_bridge to call macros in gmoccapy This is a proof of concept to allow 3rd party (hal_bridge) to call macros. The macros are run from Gmoccapy so no timing problems should occur and and pre checks or post changes can be covered. There needs to be agreement on where to put macro definitions in the INI. Gmoccapy puts them under [MACROS], iniinfo under [DISPLAY] python ZMQ module package must be available for this to work --- configs/sim/gmoccapy/gmoccapy_right_panel.ini | 8 +++- src/emc/usr_intf/gmoccapy/getiniinfo.py | 2 +- src/emc/usr_intf/gmoccapy/gmoccapy.py | 46 +++++++++++++++++-- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/configs/sim/gmoccapy/gmoccapy_right_panel.ini b/configs/sim/gmoccapy/gmoccapy_right_panel.ini index 2b7c12274c0..2816745579d 100644 --- a/configs/sim/gmoccapy/gmoccapy_right_panel.ini +++ b/configs/sim/gmoccapy/gmoccapy_right_panel.ini @@ -31,6 +31,12 @@ 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 + [FILTER] PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image PROGRAM_EXTENSION = .py Python Script @@ -75,7 +81,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/src/emc/usr_intf/gmoccapy/getiniinfo.py b/src/emc/usr_intf/gmoccapy/getiniinfo.py index 63077dc6a50..38ae7970a85 100644 --- a/src/emc/usr_intf/gmoccapy/getiniinfo.py +++ b/src/emc/usr_intf/gmoccapy/getiniinfo.py @@ -393,7 +393,7 @@ def get_tool_sensor_data(self): def get_macros(self): # lets look in the INI file, if there are any entries - macros = self.inifile.findall("MACROS", "MACRO") + macros = self.inifile.findall("DISPLAY", "MACRO") # If there are no entries we will return False if not macros: return False diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index f832f079dd4..f33d38f903a 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -406,9 +406,10 @@ def __init__(self, argv): self.widgets["rbt_view_{0}".format(view)].set_active(True) self.widgets.gremlin.set_property("view", view) - GSTAT = hal_glib.GStat() - GSTAT.connect("graphics-gcode-properties", self.on_gcode_properties) - GSTAT.connect("file-loaded", self.on_hal_status_file_loaded) + self.GSTAT = hal_glib.GStat() + 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)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -1321,6 +1322,33 @@ def _make_joints_button(self): self.joints_button_dic[name] = btn + # call INI macro (from hal_glib message) + def request_macro_call(self, data): + + # some error checking + if not self.GSTAT.is_mdi_mode(): + message = _("You must be in MDI mode to run macros") + self.dialogs.warning_dialog(self, _("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.warning_dialog(self, _("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") @@ -1362,6 +1390,8 @@ 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)) + # 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 @@ -6076,6 +6106,16 @@ def _make_hal_pins(self): hal_glib.GPin(pin).connect("value_changed", self._blockdelete) + ############################## + # 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 # ========================================================= From 6f26e73b32230984c3e1fcb2003784615620194e Mon Sep 17 00:00:00 2001 From: CMorley Date: Wed, 30 Jul 2025 20:50:47 -0700 Subject: [PATCH 11/32] gmoccapy -ability to run INI MDI commands using HAL bridge --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index f33d38f903a..ea0888b2d03 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -175,7 +175,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 @@ -1324,7 +1325,17 @@ def _make_joints_button(self): # 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('MDI command:',data,cmd) + if not cmd is None: + self.ACTION.RECORD_CURRENT_MODE() + LOG.debug("INI MDI COMMAND #: {} = {}".format(data, cmd)) + self.ACTION.CALL_INI_MDI(data) + self.ACTION.RESTORE_RECORDED_MODE() + return + # run Macros # some error checking if not self.GSTAT.is_mdi_mode(): message = _("You must be in MDI mode to run macros") @@ -6154,7 +6165,7 @@ def __setitem__(self, item, value): # Some of these libraries log when imported so logging level must already be set. import gladevcp.makepins - from gladevcp.core import Info + from gladevcp.core import Info, 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 From 063d692d6c56501163eba97ba5578cbf987a199f Mon Sep 17 00:00:00 2001 From: CMorley Date: Wed, 30 Jul 2025 20:51:51 -0700 Subject: [PATCH 12/32] gmoccapy -sample config: add INI MDI commands to test with --- configs/sim/gmoccapy/gmoccapy_right_panel.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configs/sim/gmoccapy/gmoccapy_right_panel.ini b/configs/sim/gmoccapy/gmoccapy_right_panel.ini index 2816745579d..9704bec48f9 100644 --- a/configs/sim/gmoccapy/gmoccapy_right_panel.ini +++ b/configs/sim/gmoccapy/gmoccapy_right_panel.ini @@ -37,6 +37,11 @@ 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 From 94a9524a2110139c3e59ddc1807ee1b89ccf929c Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 23 Aug 2025 06:37:17 -0700 Subject: [PATCH 13/32] halui -add gui ok/canel pins --- lib/python/bridgeui/bridge.py | 6 ++++ src/emc/usr_intf/halui.cc | 52 +++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index 862ae0244d9..27397528660 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -156,6 +156,12 @@ def cycleStart(self): 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 getMdiName(self, num): if num >len(self.INFO.MDI_COMMAND_DICT)-1: return 'None' diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index c23b55976d2..ae177345a60 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -204,8 +204,12 @@ 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) \ +\ + FIELD(hal_bit_t,gui_ok) /* pin for acknowledging dialog ok */ \ + FIELD(hal_bit_t,gui_cancel) /* pin for acknowledging dialog cancel */ \ \ FIELD(hal_float_t,units_per_mm) \ @@ -587,6 +591,34 @@ static void py_call_cyclePause() { return ; } +static void py_call_ok() { + + // check socket messages for gui ok message + pFuncWrite = PyObject_GetAttrString(pInstance, "ok"); + if (pFuncRead && 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 (pFuncRead && 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 int py_call_get_mdi_count() { int value = 0; // check socket messages for jogspeed @@ -1040,10 +1072,16 @@ int halui_hal_init(void) for (int n=0; ngui_mdi_commands[n]), comp_id, "halui.mdi-command-%s", py_call_get_mdi_name(n)); + retval = hal_pin_bit_newf(HAL_IN, &(halui_data->gui_mdi_commands[n]), comp_id, "halui.gui.mdi-command-%s", py_call_get_mdi_name(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; + hal_ready(comp_id); return 0; } @@ -2471,10 +2509,20 @@ static void check_hal_changes() // request GUI ti 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){ - printf("GUI MDI command called index: %i\n", n); + fprintf(stderr,"GUI MDI command called index: %i\n", n); py_call_request_MDI(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(); + } } // this function looks at the received NML status message From 5dbb9049f4ea0f6d250b1defc756f6c15315088b Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 23 Aug 2025 10:04:15 -0700 Subject: [PATCH 14/32] hal_glib -add ok and cancel messages --- lib/python/common/hal_glib.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py index 52eb0bae3c0..9517c3f7cb3 100644 --- a/lib/python/common/hal_glib.py +++ b/lib/python/common/hal_glib.py @@ -243,6 +243,8 @@ class _GStat(GObject.GObject): 'following-error': (GObject.SignalFlags.RUN_FIRST , GObject.TYPE_NONE,(GObject.TYPE_PYOBJECT,)), '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,)), + 'ok-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)), + 'cancel-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,)), 'macro-call-request': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), } @@ -1449,6 +1451,12 @@ def request_cycle_pause(self, data): def request_macro_call(self, data): self.emit('macro-call-request', data) + def request_ok(self, data): + self.emit('ok-request', data) + + def request_cancel(self, data): + self.emit('cancel-request', data) + ############################################# def shutdown(self): From e3e81e3f0e1a31e988c8de3afe1b5fe4485f7d2f Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 23 Aug 2025 10:05:39 -0700 Subject: [PATCH 15/32] qtdragon -react to ok and cancel messages --- share/qtvcp/screens/qtdragon/qtdragon_handler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index 58022147c4a..a52b7aca8c4 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -153,6 +153,8 @@ def __init__(self, halcomp, widgets, paths): STATUS.connect('cycle-start-request', lambda w, state :self.btn_start_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)) self.swoopPath = os.path.join(paths.IMAGEDIR,'lcnc_swoop.png') self.swoopURL = QtCore.QUrl.fromLocalFile(self.swoopPath) From 69ae7bfdeb844785f8244683bc18f1426a80c6d1 Mon Sep 17 00:00:00 2001 From: CMorley Date: Sat, 23 Aug 2025 13:57:34 -0700 Subject: [PATCH 16/32] qtdragon metric -add a control panel --- configs/sim/qtdragon/qtdragon_xyz/panel.hal | 17 ++ configs/sim/qtdragon/qtdragon_xyz/panel.ui | 237 ++++++++++++++++++ .../qtdragon/qtdragon_xyz/qtdragon_metric.ini | 3 +- 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 configs/sim/qtdragon/qtdragon_xyz/panel.hal create mode 100644 configs/sim/qtdragon/qtdragon_xyz/panel.ui diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal new file mode 100644 index 00000000000..e9407a91feb --- /dev/null +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal @@ -0,0 +1,17 @@ +net rate halui.axis.jog-speed panel.Jog-rate-f + +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 +net jog-m halui.axis.selected.minus +net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 +net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 + +net pause halui.cycle.start panel.cycle-start +net start halui.cycle.pause panel.cycle-pause + +net cancel halui.gui.cancel panel.cancel +net ok halui.gui.ok panel.ok + diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.ui b/configs/sim/qtdragon/qtdragon_xyz/panel.ui new file mode 100644 index 00000000000..d9200fc614e --- /dev/null +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.ui @@ -0,0 +1,237 @@ + + + MainWindow + + + + 0 + 0 + 313 + 467 + + + + MainWindow + + + + + + + + + Axis Selection + + + + + + X + + + axis-x + + + + + + + Y + + + axis-y + + + + + + + Z + + + axis-z + + + + + + + + + + Axis Jog + + + + + + + + + + jog-pos + + + + + + + - + + + jog-neg + + + + + + + + + + jog rate + + + + + + 300 + + + Qt::Horizontal + + + Jog-rate + + + + + + + + + + MDI Comands + + + + + + 0 + + + mdi-0 + + + + + + + 1 + + + mdi-1 + + + + + + + 2 + + + mdi-2 + + + + + + + + + + program control + + + + + + start + + + cycle-start + + + + + + + pause + + + cycle-pause + + + + + + + + + + dialog control + + + + + + ok + + + ok + + + + + + + cancel + + + cancel + + + + + + + + + + + + + + 0 + 0 + 313 + 22 + + + + + + + + PushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + Slider + QSlider +
qtvcp.widgets.simple_widgets
+
+
+ + +
diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini index a00b52ab427..ca436b20837 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini +++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini @@ -166,7 +166,8 @@ POSTGUI_HALFILE = qtdragon_postgui.hal # 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 -H panel.hal panel [HALUI] # no content From 605c67f0d97e71e01a750fe14341a321a37aeac7 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:04:00 -0700 Subject: [PATCH 17/32] iniinfo -parse ini commands in a better way mdi commands with a comma in it (ie MSG, text) would not be interpeted properly --- lib/python/common/iniinfo.py | 44 ++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/python/common/iniinfo.py b/lib/python/common/iniinfo.py index 8cdabb3a291..177548849a9 100644 --- a/lib/python/common/iniinfo.py +++ b/lib/python/common/iniinfo.py @@ -19,7 +19,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') @@ -571,7 +571,6 @@ def update(self): if self.parser.has_section('MDI_COMMAND_LIST'): try: for key in self.parser['MDI_COMMAND_LIST']: - # legacy way: list of repeat 'MDI_COMMAND=XXXX' # in this case order matters in the INI if key == 'MDI_COMMAND': @@ -594,10 +593,24 @@ def update(self): self.MDI_COMMAND_LABEL_LIST.append(k) mdidatadict['label'] = k self.MDI_COMMAND_DICT[str(count)] = mdidatadict - break # 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) @@ -605,14 +618,25 @@ def update(self): temp = self.INI.find("MDI_COMMAND_LIST",key) name = (key.replace('MDI_COMMAND_','')) mdidatadict = {} - for num,k in enumerate(temp.split(',')): - if num == 0: - mdidatadict['cmd'] = k - if len(temp.split(',')) <2: - mdidatadict['label'] = None - else: - mdidatadict['label'] = k + + # find the last colon in string or 0 + lastCmd = temp.rfind(';') + #print('l ;:',lastCmd) + if lastCmd == -1: lastCmd = 0 + + # find the last colon in string or use the string length + lastComma = temp.rfind(',', lastCmd) + #print('l comma:',lastComma,lastCmd) + if lastComma == -1: lastComma = len(temp) + + label = temp[lastComma+1:] + cmd = temp[:lastComma] + #print(temp,' 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: From 08ed08c99291988437039e06c12c031a4d841c4d Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:05:52 -0700 Subject: [PATCH 18/32] gladevcp -gth_action: add ability to run new style INI MDI commands named INI MDI commands were not recognised. --- lib/python/gladevcp/gtk_action.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/python/gladevcp/gtk_action.py b/lib/python/gladevcp/gtk_action.py index c7eb96d3671..548a3255acc 100644 --- a/lib/python/gladevcp/gtk_action.py +++ b/lib/python/gladevcp/gtk_action.py @@ -191,14 +191,23 @@ 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): 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(';') self.ensure_mode(linuxcnc.MODE_MDI) for code in (mdi_list): From 65de817678364a0b9539e02ddbb170475457d8a2 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:07:38 -0700 Subject: [PATCH 19/32] qtvcp -dialog widget: add status message control of tool change dialog you can use STATUS messages to 'press' ok or cancel --- lib/python/qtvcp/widgets/dialog_widget.py | 46 +++++++++++++++++------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/python/qtvcp/widgets/dialog_widget.py b/lib/python/qtvcp/widgets/dialog_widget.py index 55c199baf8f..958ba61f4bb 100644 --- a/lib/python/qtvcp/widgets/dialog_widget.py +++ b/lib/python/qtvcp/widgets/dialog_widget.py @@ -398,6 +398,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) @@ -440,6 +441,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): @@ -490,7 +493,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() @@ -498,10 +501,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() @@ -517,6 +539,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, @@ -558,16 +590,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) - ############################################ # ********************** From 0e3f7fcad5d16197cb60e30b9aad5ea24b7b605b Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:13:51 -0700 Subject: [PATCH 20/32] qtdragon -add status control of toolchange dialog so you can use halui to accept the toolchange --- .../screens/qtdragon/qtdragon_handler.py | 35 +++++-------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/share/qtvcp/screens/qtdragon/qtdragon_handler.py b/share/qtvcp/screens/qtdragon/qtdragon_handler.py index a52b7aca8c4..adc2cc604cb 100644 --- a/share/qtvcp/screens/qtdragon/qtdragon_handler.py +++ b/share/qtvcp/screens/qtdragon/qtdragon_handler.py @@ -880,6 +880,7 @@ def lastRunLine(self, line): # called from hal_glib to run macros from external event def request_macro_call(self, data): + print('macro request:',data) if not STATUS.is_mdi_mode(): self.add_status(_translate("HandlerClass",'Machine must be in MDI mode to run macros'), CRITICAL) return @@ -904,6 +905,8 @@ def request_macro_call(self, data): self.add_status(_translate("HandlerClass",'Running macro: {} {}'.format(key, text))) button.click() break + else: + self.add_status(_translate("HandlerClass","can't find button for macro: {}".format(data))) ####################### # CALLBACKS FROM FORM # @@ -2049,9 +2052,14 @@ def external_mpg(self, count): def dialog_ext_control(self, pin, value, answer): if value: + # handler defined dialog? if not self._dialog_message is None: name = self._dialog_message.get('NAME') STATUS.emit('dialog-update',{'NAME':name,'response':answer}) + else: + # tool change dialog? + if self.w.toolDialog_.isVisible(): + STATUS.emit('dialog-update',{'NAME':'TOOLCHANGE','response':answer}) def log_version(self): if INFO.RIP_FLAG: @@ -2067,33 +2075,6 @@ def log_version(self): self.add_status(mess, CRITICAL,noLog=True) STATUS.emit('update-machine-log', mess, None) - # called from hal_glib to run macros from external event - def request_macro_call(self, data): - if not STATUS.is_mdi_mode(): - self.add_status(_translate("HandlerClass",'Machine must be in MDI mode to run macros'), CRITICAL) - return - - for b in range(0,10): - button = self.w['macrobutton{}'.format(b)] - # prefer named INI MDI commands - key = button.property('ini_mdi_key') - code = INFO.get_ini_mdi_command(key) - if key == '' or code is None: - # fallback to legacy nth line - key = button.property('ini_mdi_number') - code = INFO.get_ini_mdi_command(key) - if code is None: - continue - if str(key) == data: - #print('match',button.objectName()) - text = button.text().replace('\n',' ') - self.add_status(_translate("HandlerClass",'Running macro: {} {}'.format(key, text))) - try: - button.click() - except Exception as e: - self.add_status(_translate("HandlerClass",'Running macro: {} {}\n{}'.format(key, text, e))) - break - ##################### # KEY BINDING CALLS # ##################### From 129144769de283660aaff59136828f7622757bc3 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:16:35 -0700 Subject: [PATCH 21/32] gmoccapy -add gstat message control of start and pause --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index ea0888b2d03..2f361e73fd0 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -411,6 +411,8 @@ def __init__(self, argv): 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)) # get if run from line should be used self.run_from_line = self.prefs.getpref("run_from_line", "no_run", str) @@ -1323,6 +1325,14 @@ def _make_joints_button(self): self.joints_button_dic[name] = btn + def request_start(self,data): + print('start') + self.widgets.btn_run.emit('clicked') + + def request_pause(self,data): + print('pause') + self.widgets.tbtn_pause.emit('clicked') + # call INI macro (from hal_glib message) def request_macro_call(self, data): # if MDI command change to MDI and run From 5428a3f410770099285ecfd81557c72be837b935 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:19:59 -0700 Subject: [PATCH 22/32] gmoccapy -add a halui test config with a sim test panel --- configs/sim/gmoccapy/gmoccapy_halui_test.ini | 225 ++++++++++++ configs/sim/gmoccapy/panel.hal | 24 ++ configs/sim/gmoccapy/panel.ui | 365 +++++++++++++++++++ 3 files changed, 614 insertions(+) create mode 100644 configs/sim/gmoccapy/gmoccapy_halui_test.ini create mode 100644 configs/sim/gmoccapy/panel.hal create mode 100644 configs/sim/gmoccapy/panel.ui 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/panel.hal b/configs/sim/gmoccapy/panel.hal new file mode 100644 index 00000000000..d48117a20ff --- /dev/null +++ b/configs/sim/gmoccapy/panel.hal @@ -0,0 +1,24 @@ +net rate halui.axis.jog-speed panel.Jog-rate-f + +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-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 + diff --git a/configs/sim/gmoccapy/panel.ui b/configs/sim/gmoccapy/panel.ui new file mode 100644 index 00000000000..c671b279767 --- /dev/null +++ b/configs/sim/gmoccapy/panel.ui @@ -0,0 +1,365 @@ + + + MainWindow + + + + 0 + 0 + 313 + 467 + + + + 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 + + + + + + 300 + + + Qt::Horizontal + + + Jog-rate + + + + + + + + + + MDI Comands + + + + + + 0 + + + mdi-0 + + + + + + + 1 + + + mdi-1 + + + + + + + 2 + + + mdi-2 + + + + + + + + + + Mode Comands + + + + + + Manual + + + true + + + true + + + manual-mode + + + true + + + true + + + true + + + + + + + MDI + + + true + + + true + + + mdi-mode + + + true + + + true + + + true + + + + + + + Auto + + + true + + + true + + + auto-mode + + + true + + + true + + + true + + + + + + + + + + program control + + + + + + start + + + cycle-start + + + + + + + pause + + + cycle-pause + + + + + + + Abort + + + cycle-abort + + + + + + + + + + dialog control + + + + + + ok + + + ok + + + + + + + cancel + + + cancel + + + + + + + + + + + + + + 0 + 0 + 313 + 22 + + + + + + + + PushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + Slider + QSlider +
qtvcp.widgets.simple_widgets
+
+
+ + +
From 149dfc327ab60dd6c3dfb604d5d6964263f4b2e3 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 10:24:15 -0700 Subject: [PATCH 23/32] qtdragon -update test config test panel --- configs/sim/qtdragon/qtdragon_xyz/panel.hal | 24 ++-- configs/sim/qtdragon/qtdragon_xyz/panel.ui | 130 +++++++++++++++++- .../qtdragon/qtdragon_xyz/qtdragon_metric.ini | 4 +- 3 files changed, 144 insertions(+), 14 deletions(-) diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal index e9407a91feb..a5c68d8da0c 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/panel.hal +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal @@ -1,17 +1,19 @@ -net rate halui.axis.jog-speed panel.Jog-rate-f +net rate halui.axis.jog-speed panel.Jog-rate-f -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 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 -net jog-m halui.axis.selected.minus -net m0 halui.gui.mdi-command-MACRO0 panel.mdi-0 -net m1 halui.gui.mdi-command-MACRO1 panel.mdi-1 +net jog-p halui.axis.selected.plus panel.jog-pos +net jog-m halui.axis.selected.minus panel.jog-neg -net pause halui.cycle.start panel.cycle-start -net start halui.cycle.pause panel.cycle-pause +#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 cancel halui.gui.cancel panel.cancel +net pause halui.cycle.start panel.cycle-start +net start halui.cycle.pause panel.cycle-pause + +net cancel halui.gui.cancel panel.cancel net ok halui.gui.ok panel.ok diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.ui b/configs/sim/qtdragon/qtdragon_xyz/panel.ui index d9200fc614e..c671b279767 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/panel.ui +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.ui @@ -23,11 +23,33 @@ Axis Selection + + + + None + + + true + + + true + + + axis-none + + + X + + true + + + true + axis-x @@ -38,6 +60,12 @@ Y + + true + + + true + axis-y @@ -48,6 +76,12 @@ Z + + true + + + true + axis-z @@ -146,6 +180,90 @@ + + + + Mode Comands + + + + + + Manual + + + true + + + true + + + manual-mode + + + true + + + true + + + true + + + + + + + MDI + + + true + + + true + + + mdi-mode + + + true + + + true + + + true + + + + + + + Auto + + + true + + + true + + + auto-mode + + + true + + + true + + + true + + + + + + @@ -163,7 +281,7 @@ - + pause @@ -172,6 +290,16 @@ + + + + Abort + + + cycle-abort + + + diff --git a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini index ca436b20837..e7a674f81b2 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini +++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini @@ -10,7 +10,7 @@ DEBUG = 0x00000000 # sets qtdragon as screen. for debug output to terminal add -d or -v # sets window title # sets icon in task manager -DISPLAY = qtvcp qtdragon +DISPLAY = qtvcp -d qtdragon TITLE = QtDragon XYZ Metric ICON = silver_dragon.png @@ -167,7 +167,7 @@ POSTGUI_HALFILE = qtdragon_postgui.hal # 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 -H panel.hal panel +POSTGUI_HALCMD = loadusr qtvcp -a -H panel.hal panel [HALUI] # no content From 7ea7c107d1ba045c66ed09872cf176380b9accd9 Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 18:10:59 -0700 Subject: [PATCH 24/32] qtvcp/gladevcp -action: add ability; return to mode after INI mdi --- lib/python/gladevcp/gtk_action.py | 12 +++++++++++- lib/python/qtvcp/qt_action.py | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/python/gladevcp/gtk_action.py b/lib/python/gladevcp/gtk_action.py index 548a3255acc..ab22f063f7b 100644 --- a/lib/python/gladevcp/gtk_action.py +++ b/lib/python/gladevcp/gtk_action.py @@ -191,7 +191,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) @@ -209,11 +209,21 @@ def CALL_INI_MDI(self, key): 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) diff --git a/lib/python/qtvcp/qt_action.py b/lib/python/qtvcp/qt_action.py index 2c5f18cedb0..3d57ef050c8 100644 --- a/lib/python/qtvcp/qt_action.py +++ b/lib/python/qtvcp/qt_action.py @@ -227,7 +227,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) @@ -244,11 +244,21 @@ 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)) 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) From 8f622876985a34eec866003d741ef6ec6454909b Mon Sep 17 00:00:00 2001 From: CMorley Date: Mon, 1 Sep 2025 21:27:49 -0700 Subject: [PATCH 25/32] gmoccapy -used new mode return INI MDI function --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 2f361e73fd0..45f743925b2 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -1339,10 +1339,8 @@ def request_macro_call(self, data): cmd = self.INFO.get_ini_mdi_command(data) print('MDI command:',data,cmd) if not cmd is None: - self.ACTION.RECORD_CURRENT_MODE() LOG.debug("INI MDI COMMAND #: {} = {}".format(data, cmd)) - self.ACTION.CALL_INI_MDI(data) - self.ACTION.RESTORE_RECORDED_MODE() + self.ACTION.CALL_INI_MDI(data,mode_return = True) return # run Macros From cb3fa3a596e8dec2886fc15f204ebe6ffab17b36 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 5 Sep 2025 20:39:53 -0700 Subject: [PATCH 26/32] halui -fix memory leaks --- lib/python/bridgeui/bridge.py | 2 +- src/emc/usr_intf/halui.cc | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/python/bridgeui/bridge.py b/lib/python/bridgeui/bridge.py index 27397528660..f2377f26100 100644 --- a/lib/python/bridgeui/bridge.py +++ b/lib/python/bridgeui/bridge.py @@ -97,7 +97,7 @@ def init_read(self): self.readSocket.connect(self.readAddress) # callback from ZMQ read socket - def readMsg(self, msg): + def readMsg(self): if self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN: while self.readSocket.getsockopt(zmq.EVENTS) & zmq.POLLIN: # get raw message diff --git a/src/emc/usr_intf/halui.cc b/src/emc/usr_intf/halui.cc index ae177345a60..518de941721 100644 --- a/src/emc/usr_intf/halui.cc +++ b/src/emc/usr_intf/halui.cc @@ -1873,9 +1873,8 @@ static void py_call_axis_jogspeed(double speed) if (pValue == NULL){ fprintf(stderr, "halui bridge: writeMsg function failed: returned NULL\n"); if (PyErr_Occurred()) PyErr_Print(); - }else{ - Py_DECREF(pValue); } + Py_DECREF(pValue); }else{ if (PyErr_Occurred()) PyErr_Print(); @@ -1922,9 +1921,8 @@ static void py_call_axis_changed( int axis) if (pValue == NULL){ fprintf(stderr, "halui bridge: writeMsg function failed: returned NULL\n"); if (PyErr_Occurred()) PyErr_Print(); - }else{ - Py_DECREF(pValue); } + Py_DECREF(pValue); }else{ if (PyErr_Occurred()) PyErr_Print(); @@ -1942,9 +1940,8 @@ static void py_call_request_MDI( int index) if (pValue == NULL){ fprintf(stderr, "halui bridge: runIndexedMacro function failed: returned NULL\n"); if (PyErr_Occurred()) PyErr_Print(); - }else{ - Py_DECREF(pValue); } + Py_DECREF(pValue); }else{ if (PyErr_Occurred()) PyErr_Print(); @@ -1970,8 +1967,9 @@ static void check_hal_changes() // get python to process socket messages pFuncRead = PyObject_GetAttrString(pInstance, "readMsg"); + if (pFuncRead && PyCallable_Check(pFuncRead)) { - pValue = PyObject_CallFunction(pFuncRead, "O", pClass); + pValue = PyObject_CallNoArgs(pFuncRead); if (pValue == NULL){ fprintf(stderr, "Halui Bridge: readMsg function failed: returned NULL\n"); } @@ -1983,6 +1981,8 @@ static void check_hal_changes() 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(); @@ -2764,6 +2764,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "bridge: Failed to load \"%s\"\n", "pyui"); exit(1); } + Py_DECREF(pClass); // get configuration information if (0 != iniLoad(emc_inifile)) { From f826df6bebd31e8cdeb7f955e8004970fa2b2ef8 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 5 Sep 2025 20:40:47 -0700 Subject: [PATCH 27/32] qtdragon -test files update --- configs/sim/qtdragon/qtdragon_xyz/panel.hal | 11 ++++++++--- configs/sim/qtdragon/qtdragon_xyz/panel.ui | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.hal b/configs/sim/qtdragon/qtdragon_xyz/panel.hal index a5c68d8da0c..04bc14e8e26 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/panel.hal +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.hal @@ -1,4 +1,4 @@ -net rate halui.axis.jog-speed panel.Jog-rate-f +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 @@ -7,12 +7,17 @@ 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 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/qtdragon/qtdragon_xyz/panel.ui b/configs/sim/qtdragon/qtdragon_xyz/panel.ui index c671b279767..74a4afbef7d 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/panel.ui +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.ui @@ -6,8 +6,8 @@ 0 0 - 313 - 467 + 404 + 560 @@ -126,15 +126,15 @@ - - - 300 - + Qt::Horizontal - Jog-rate + jog-rate + + + true @@ -341,7 +341,7 @@ 0 0 - 313 + 404 22 @@ -355,9 +355,9 @@
qtvcp.widgets.simple_widgets
- Slider + StatusSlider QSlider -
qtvcp.widgets.simple_widgets
+
qtvcp.widgets.status_slider
From 01e287ab98e622f2eeeaf36198461e46fceff6ca Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 26 Sep 2025 22:09:20 -0700 Subject: [PATCH 28/32] hal_glib -add a function to run the gobject mainloop once so GUIs not basd in gobject can run the message system --- lib/python/common/hal_glib.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/python/common/hal_glib.py b/lib/python/common/hal_glib.py index 9517c3f7cb3..9d45601c7bc 100644 --- a/lib/python/common/hal_glib.py +++ b/lib/python/common/hal_glib.py @@ -347,6 +347,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() @@ -355,8 +360,8 @@ def init_write_socket(self): self.write_socket.bind(self.writeAddress) LOG.debug('hal_glib write socket available: {}'.format(self.writeAddress)) self.write_available = True - except: - LOG.debug('hal_glib write socket not available') + except Exception as e: + LOG.debug('hal_glib write socket not available\n {}'.format(e)) self.write_available = False # convert and actually send out the message From 8e6df68d81173adefac1513ec02bb78777f31444 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 26 Sep 2025 22:27:26 -0700 Subject: [PATCH 29/32] axis sinm -add a sim test for halui messages --- configs/sim/axis/axis_halui_test.ini | 225 +++++++++++++++++ configs/sim/axis/gstatmessages.py | 66 +++++ configs/sim/axis/panel.hal | 24 ++ configs/sim/axis/panel.ui | 365 +++++++++++++++++++++++++++ 4 files changed, 680 insertions(+) create mode 100644 configs/sim/axis/axis_halui_test.ini create mode 100644 configs/sim/axis/gstatmessages.py create mode 100644 configs/sim/axis/panel.hal create mode 100644 configs/sim/axis/panel.ui 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..74a4afbef7d --- /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 + + + true + + + true + + + manual-mode + + + true + + + true + + + true + + + + + + + MDI + + + true + + + true + + + mdi-mode + + + true + + + true + + + true + + + + + + + Auto + + + true + + + true + + + auto-mode + + + true + + + true + + + true + + + + + + + + + + program control + + + + + + start + + + cycle-start + + + + + + + pause + + + cycle-pause + + + + + + + Abort + + + cycle-abort + + + + + + + + + + dialog control + + + + + + ok + + + ok + + + + + + + cancel + + + cancel + + + + + + + + + + + + + + 0 + 0 + 404 + 22 + + + + + + + + PushButton + QPushButton +
qtvcp.widgets.simple_widgets
+
+ + StatusSlider + QSlider +
qtvcp.widgets.status_slider
+
+
+ + +
From c3fe8e75d435a42598436f6c0c35ee8574ba4e65 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 26 Sep 2025 22:34:12 -0700 Subject: [PATCH 30/32] qtdragon -add halui test sim --- .../qtdragon_xyz/qtdragon_halui_test.ini | 267 ++++++++++++++++++ .../qtdragon/qtdragon_xyz/qtdragon_metric.ini | 5 +- 2 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini 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..e7a674f81b2 --- /dev/null +++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_halui_test.ini @@ -0,0 +1,267 @@ +# 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 oage up to 10 possible +MDI_COMMAND_MACRO0 = G0 Z25;X0 Y0;Z0, Goto\nUser\nZero +MDI_COMMAND_MACRO1 = G53 G0 Z0;G53 G0 X0 Y0,Goto\nMachn\nZero + +[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 + +# 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 e7a674f81b2..a00b52ab427 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini +++ b/configs/sim/qtdragon/qtdragon_xyz/qtdragon_metric.ini @@ -10,7 +10,7 @@ DEBUG = 0x00000000 # 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 +DISPLAY = qtvcp qtdragon TITLE = QtDragon XYZ Metric ICON = silver_dragon.png @@ -166,8 +166,7 @@ POSTGUI_HALFILE = qtdragon_postgui.hal # 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 From f3719758cb39179bcb036024148c0359d65fed66 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 17 Oct 2025 21:47:47 -0700 Subject: [PATCH 31/32] test panel - make mode buttons not checkable The indicator shows state, so we don't need to chow checked state. in a real panel the buttons would be momentary buttons anyways --- configs/sim/axis/panel.ui | 6 +++--- configs/sim/gmoccapy/panel.ui | 12 ++++++------ configs/sim/qtdragon/qtdragon_xyz/panel.ui | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/configs/sim/axis/panel.ui b/configs/sim/axis/panel.ui index 74a4afbef7d..7264d58f6d0 100644 --- a/configs/sim/axis/panel.ui +++ b/configs/sim/axis/panel.ui @@ -192,7 +192,7 @@ Manual - true + false true @@ -217,7 +217,7 @@ MDI - true + false true @@ -242,7 +242,7 @@ Auto - true + false true diff --git a/configs/sim/gmoccapy/panel.ui b/configs/sim/gmoccapy/panel.ui index c671b279767..6ed71542f13 100644 --- a/configs/sim/gmoccapy/panel.ui +++ b/configs/sim/gmoccapy/panel.ui @@ -6,8 +6,8 @@ 0 0 - 313 - 467 + 404 + 537 @@ -195,7 +195,7 @@ true - true + false manual-mode @@ -220,7 +220,7 @@ true - true + false mdi-mode @@ -242,7 +242,7 @@ Auto - true + false true @@ -341,7 +341,7 @@ 0 0 - 313 + 404 22 diff --git a/configs/sim/qtdragon/qtdragon_xyz/panel.ui b/configs/sim/qtdragon/qtdragon_xyz/panel.ui index 74a4afbef7d..7264d58f6d0 100644 --- a/configs/sim/qtdragon/qtdragon_xyz/panel.ui +++ b/configs/sim/qtdragon/qtdragon_xyz/panel.ui @@ -192,7 +192,7 @@ Manual - true + false true @@ -217,7 +217,7 @@ MDI - true + false true @@ -242,7 +242,7 @@ Auto - true + false true From b204443fc12f6e9536938a7dd6019614d2c4422b Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 17 Oct 2025 21:50:03 -0700 Subject: [PATCH 32/32] gmoccapy -getinfo.py: fix macro search title this must have been fix in master after I branched --- src/emc/usr_intf/gmoccapy/getiniinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emc/usr_intf/gmoccapy/getiniinfo.py b/src/emc/usr_intf/gmoccapy/getiniinfo.py index 38ae7970a85..63077dc6a50 100644 --- a/src/emc/usr_intf/gmoccapy/getiniinfo.py +++ b/src/emc/usr_intf/gmoccapy/getiniinfo.py @@ -393,7 +393,7 @@ def get_tool_sensor_data(self): def get_macros(self): # lets look in the INI file, if there are any entries - macros = self.inifile.findall("DISPLAY", "MACRO") + macros = self.inifile.findall("MACROS", "MACRO") # If there are no entries we will return False if not macros: return False