From 0f8f1cc7935bf0bcf4e3e320a38c8ebe01a7ca53 Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 22 Dec 2023 16:06:46 -0800 Subject: [PATCH 1/2] gladevcp -add a zmq network object for communicating between GUIs / external programs --- lib/python/gladevcp/zmqnetwork.py | 136 ++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 lib/python/gladevcp/zmqnetwork.py diff --git a/lib/python/gladevcp/zmqnetwork.py b/lib/python/gladevcp/zmqnetwork.py new file mode 100644 index 00000000000..a285ce49af3 --- /dev/null +++ b/lib/python/gladevcp/zmqnetwork.py @@ -0,0 +1,136 @@ +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import GLib + +from qtvcp import logger +LOG = logger.getLogger(__name__) +# Force the log level for this module +#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL + +# only import zmq if needed and present +def import_ZMQ(): + try: + import zmq + except: + LOG.warning('Problem importing zmq - Is python3-zmq installed?') + # nope no messaging for you + return False + else: + import json + # since we imported in a function we need to globalize + global zmq + global json + # imports are good - go ahead and setup messaging + return True + +class ZMQSendReceive(): + def __init__(self, instance = None, topic = b""): + self._inst = instance + self.add_send_zmq = False + self.add_receive_zmq = True + + self._zmq_sub_subscribe_name = topic + self._zmq_sub_socket_address = "tcp://127.0.0.1:5690" + self._zmq_pub_socket_address = "tcp://127.0.0.1:5690" + +#################### +# initialize +#################### + + def openPublish(self): + self.add_send_zmq = True + self.init_zmq_publish() + + def openSubscribe(self): + self.add_receive_zmq = True + self.init_zmq_subscribe() + +###################################### +# subscribe +###################################### + + def init_zmq_subscribe(self): + if import_ZMQ(): + try: + self._zmq_sub_context = zmq.Context() + self._zmq_sub_sock = self._zmq_sub_context.socket(zmq.SUB) + self._zmq_sub_sock.connect(self._zmq_sub_socket_address) + self._zmq_sub_sock.setsockopt(zmq.SUBSCRIBE, self._zmq_sub_subscribe_name) + zmq_fd = self._zmq_sub_sock.getsockopt(zmq.FD) + channel = GLib.IOChannel.unix_new(zmq_fd) + GLib.io_add_watch(channel, GLib.IO_IN, self.zmq_callback, self._zmq_sub_sock) + + except Exception as e: + LOG.exception('zmq subscribe to message setup error: {}'.format(e)) + + def zmq_callback(self, fd, condition, zmq_socket): + while zmq_socket.getsockopt(zmq.EVENTS) & zmq.POLLIN: + (channel, msg) = zmq_socket.recv_multipart() + self.call_function(channel, msg) + return True + + # convert message to a function call + def call_function(self, topic, data): + # convert from json object to python object + y = json.loads(data) + # get the function name + function = y.get('FUNCTION') + # get the arguments + arguments = y.get('ARGS') + if self._inst is None: + print('{} Sent ZMQ Message:{} {}'.format(topic,function,arguments)) + return + # call handler function with arguments + try: + # call self._inst (a function) with all arguments + if callable(self._inst): + self._inst(topic,function,arguments) + else: + # directly call the function of self._inst (a class instance) + self._inst[function](arguments) + except Exception as e: + LOG.error('zmq message parcing error: {}'.format(e)) + LOG.error('{} {}'.format(function, arguments)) + +################################ +# Publish +################################ + + def init_zmq_publish(self): + if import_ZMQ(): + try: + self._zmq_pub_context = zmq.Context() + self._zmq_pub_socket = self._zmq_pub_context.socket(zmq.PUB) + self._zmq_pub_socket.bind(self._zmq_pub_socket_address) + except Exception as e: + LOG.error('zmq publish message setup error: {}'.format(e)) + + def zmq_write_message(self, args,topic = 'gladevcp'): + if self.add_send_zmq: + try: + message = json.dumps(args) + LOG.debug('Sending ZMQ Message:{} {}'.format(topic,message)) + self._zmq_pub_socket.send_multipart( + [bytes(topic.encode('utf-8')), + bytes((message).encode('utf-8'))]) + except Exception as e: + LOG.error('zmq message sending error: {}'.format(e)) + else: + LOG.info('ZMQ Message not enabled. message:{} {}'.format(topic,args)) + +#################################### +# Testing +#################################### +if __name__ == "__main__": + + n = ZMQSendReceive() + n.init_zmq_subscribe() + + # loop till exit + try: + GLib.MainLoop().run() + except KeyboardInterrupt: + raise SystemExit + + + From 23d0f1b9860e2d90b14dca44e8b6be2b845ca1de Mon Sep 17 00:00:00 2001 From: CMorley Date: Fri, 22 Dec 2023 16:08:22 -0800 Subject: [PATCH 2/2] gmoccapy -listen for zmq messages to call functons --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index 4645da8fb3c..372274f3db8 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -49,6 +49,8 @@ from time import strftime # needed for the clock in the GUI #from Gtk._Gtk import main_quit +from gladevcp.zmqnetwork import ZMQSendReceive + # Throws up a dialog with debug info when an error is encountered def excepthook(exc_type, exc_obj, exc_tb): try: @@ -167,6 +169,9 @@ def __init__(self, argv): self.stat.poll() self.error_channel.poll() + self.zmqmessage = ZMQSendReceive(self.external_call) + self.zmqmessage.init_zmq_subscribe() + self.builder = Gtk.Builder() # translation of the glade file will be done with self.builder.set_translation_domain("gmoccapy") @@ -3968,6 +3973,21 @@ def _update_vc(self): text = "Vc= {0:.2f}".format(vc) self.widgets.lbl_vc.set_text(text) + def external_reload(self, path): + print('gmoccapy external reload:',path) + if path is None: + self.widgets.hal_action_reload.emit("activate") + else: + # use a little hack to load a specific file + self.widgets.hal_action_reload._load_file(path) + + # qualify a ZMQ message and if good, call the function + def external_call(self, topic, function, args): + print('gmoccapy ext call:',topic, function, args) + # allow reloading of files + if function == 'external_reload': + self.external_reload(args[0]) + def on_rbt_forward_clicked(self, widget, data=None): if widget.get_active(): widget.set_image(self.widgets.img_spindle_forward_on) @@ -5712,6 +5732,12 @@ def _make_hal_pins(self): pin = self.halcomp.newpin("blockdelete", hal.HAL_BIT, hal.HAL_IN) hal_glib.GPin(pin).connect("value_changed", self._blockdelete) + def __getitem__(self, item): + return getattr(self, item) + + def __setitem__(self, item, value): + return setattr(self, item, value) + # Hal Pin Handling End # =========================================================