Properly exiting a GladeVCP panel embedded in Axis

More
09 Jan 2015 06:10 - 09 Jan 2015 06:11 #54809 by tnl.lambert
I'm working with a GladeVCP panel embedded in axis - same project as my last thread. I've successfully implemented a modbus protocol in a background python thread that talks over USB to a serial port.

When I close the axis window, several errors are thrown:
Starting LinuxCNC...
INFO CLASSICLADDER-   No ladder GUI requested-Realtime runs till HAL closes.
INFO CLASSICLADDER---I/O modbus master closed!
INFO CLASSICLADDER---I/O modbus master (Ethernet) init ok !
Exiting Main Thread
Starting Modbus-Thread-1
Start Time: 1420754623.674504
Xlib.protocol.request.QueryExtension
Xlib.protocol.request.QueryExtension

/usr/bin/gladevcp:292: GtkWarning: GdkWindow 0x3400003 unexpectedly destroyed
  gtk.main()
quit with close
**** GLADE VCP ERROR:    X Protocol Error: 3
Shutting down and cleaning up LinuxCNC...
Running HAL shutdown script
ULAPI: WARNING: module 'HAL_classicladder' failed to delete shmem 03
ERROR CLASSICLADDER-   Error intializing classicladder user module.
ERROR:  Can't remove RTAI modules, kill the following process(es) first
                     USER        PID ACCESS COMMAND
/dev/rtai_shm:       stan       1548 ....m gladevcp

I think that what is happening is that the thread is staying alive after the window is closed. The 'quit with close' line in the above text is where my gtk exit handler is called, but it looks like the first error occurs before that. Or maybe I'm missing something - perhaps I need a custom shutdown script? Any info would be appreciated.
Last edit: 09 Jan 2015 06:11 by tnl.lambert.

Please Log in or Create an account to join the conversation.

More
09 Jan 2015 19:33 #54818 by andypugh

perhaps I need a custom shutdown script? Any info would be appreciated.


If you do have a shutdown script then you can have your GladeVCP panels save state on exit (the GladeVCP docs explain how to do this). So it is probably worth trying this anyway.

Please Log in or Create an account to join the conversation.

More
10 Jan 2015 00:43 - 10 Jan 2015 00:46 #54828 by tnl.lambert
I have tried adding the on_unix_signal handler, and I've tried saving state - I think that the problem is tied to the fact that the gladevcp process sticks around after axis closes (and after the error messages are printed).

I have a destroy handler that calls gtk.main_exit(), but maybe it's just a matter of my threads failing to terminate? I have also looked through the forums, specifically this one: forum

I've attached my code if you don't mind taking a look:
#!/usr/bin/env python

import hal
import gtk
import gobject
import glib
import linuxcnc
import serial
import threading
import sys
import time

numerr = 0;
h = hal.component("IRB")
h.newpin("go", hal.HAL_BIT, hal.HAL_IN)
h.ready()
mainclosed = 0

try:
    s = linuxcnc.stat() # create a connection to the status channel
    s.poll() # get current values
except linuxcnc.error, detail:
    print "LINUXCNC error", detail
    gtk.main_quit()
    sys.exit(1)
#for x in dir(s):
#    if not x.startswith('_'):
#        print x, getattr(s,x)

class myThread (threading.Thread):
    def __init__(self, threadID, name, timeout):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.timeout = timeout
    def run(self):
        print "Starting " + self.name
        modbusInit(self.name, self.timeout)
        print "Exiting " + self.name

def modbusInit(threadName,timeoutIn):
	ser = serial.Serial('/dev/ttyUSB0',timeout = timeoutIn)
	outputByte = 165
	numerr = 0
	inSize = 1
	
	#calcXor()
	
	print "Start Time: %f" % time.time()
	while numerr < 100:
		if mainclosed == 1:
			print "Thread Closing"
			thread.exit()
		if h.go == 1:
			print "working thread 1: %f" % time.time()
			#try:
			if sendFc16(165,1,2,[102,122],ser) == 1:
				print "Communication Success"
			
			#time.sleep(1)
			
			#outCRC = getCRC(outputByte)
			
			#if writeByte(chr(outputByte),ser) == 1:
				#print "inside if"
				#writeByte(chr(65),ser)
				#writeByte(chr(5),ser)
				
				
					#print "written"
					#time.sleep(0.5)
					#inputByte = readByte(inSize,ser)
				#print "direct input =" + inputByte
				#print "inputByte = %d" % ord(inputByte) 
				#outputByte += 1
			#except:
			#	print "Comm Error"
		
			#checkCRC()
			
	ser.close()
	
#def calcXor():
#	print 'Writing File'
#	f = open('xortable', 'w')
#	f.write('[')
#	for i in range(0,256):
#		f.write('[')
#		for j in range(0,255):
#			f.write(str(i ^ j) + ',')
#		f.write(str(i ^ 255) + '],')
#	f.write('];')
#	f.close

def writeByte(outByte,outPort):
	#outPort.flushInput()
	#outPort.flushOutput()
	return outPort.write(outByte)
	
def readByte(readSize,readPort):
	inputByte = readPort.read(readSize)
	while len(inputByte)==0:
		inputByte = readPort.read(readSize)
	return inputByte
	
def getCRC(outMessage):
	CRCFull = 0xFFFF 
	#0xFFFF;
	CRC = bytearray(2);
	for i in range(0,len(outMessage)-2):
		CRCFull = CRCFull ^ outMessage[i]
		#print "CRCFull1 =" + bin(CRCFull) + "i = %d" % i 
		#CRCFull = CRCFull % 2**16
		for j in range(0,8):
			CRCLSB = CRCFull & 0x0001
			CRCFull = (CRCFull >> 1) & 0x7FFF
			#0x7FFF
			#print "CRCFull2 =" + bin(CRCFull) + "CRCLSB = %d" % CRCLSB
			#CRCFull = CRCFull % 2**16
			if CRCLSB==1:
				CRCFull = CRCFull ^ 0xA001
				#0xA001
				#print "CRCFull3 =" + bin(CRCFull) 
				#CRCFull = CRCFull % 2**16
	CRC[1] = (CRCFull >> 8) & 0xFF
	CRC[0] = CRCFull & 0xFF
	return CRC

def buildMessage(address,type,start,registers,message):
	message[0] = address
	message[1] = type
	message[2] = start >> 8
	message[3] = start
	message[4] = registers >> 8
	message[5] = registers
	outputCRC = getCRC(message)
	#print "mess0 is: %d" % message[0]
	#print "mess1 is: %d" % message[1]
	#print "mess2 is: %d" % message[2]
	#print "mess3 is: %d" % message[3]
	#print "mess4 is: %d" % message[4]
	#print "mess5 is: %d" % message[5]
	#print "mess6 is: %d" % message[6]
	#print "mess7 is: %d" % message[7]
	#print "mess8 is: %d" % message[8]
	#print "mess9 is: %d" % message[9]
	#print "mess10 is: %d" % message[10]
	message[len(message)-2] = outputCRC[0]
	message[len(message)-1] = outputCRC[1]
	print "CRC0 is: %d" % message[11]
	print "CRC1 is: %d" % message[12]
	
def sendFc16(address,start,registers,values,port):	
	if port.isOpen():
		port.flushInput()
		port.flushOutput()
		message = bytearray(9 + 2 * registers)
		response = bytearray(8)
		message[6] = registers * 2
		for i in range(0,registers):
			message[7 + 2*i] = values[i] >> 8
			message[8 + 2*i] = values[i]
			#print "i = %d" % i
		buildMessage(address,16,start,registers,message)
		#print "message length = %d" % len(message)
		writeMessage(port,message)
		#print "Message Written"
		response = getResponse(port)
		#print "res7 is: %d" % response[7]
		#print "After Response"
		if checkResponse(response):
			print "CRC Match"
			return 1
	return 0

def writeMessage(port,message):
	for i in range(0,len(message)):
		writeByte(chr(message[i]),port)
		#print "chrmess " + chr(message[i])
	#print "Write Complete"

def getResponse(port):
	response = bytearray(8)
	for i in range(0,8):
		#print "Before Read"
		response[i] = readByte(1,port)
		#print "resi is: %d" % response[i]
	return response

def checkResponse(response):
	CRC = getCRC(response)
	#print "crc0 is: %d" % CRC[0]
	return CRC[0] == response[len(response) - 2] and CRC[1] == response[len(response) - 1]
	
class HandlerClass:

	def __init__(self,halcomp,builder,useropts):
		filename = "/home/stan/Documents/gladetests/test1.ui"
		builder = gtk.Builder()
		builder.add_from_file(filename)
		builder.connect_signals(self)
	def on_hal_table1_destroy(self, widget, data=None):
		print "quit with close"
		mainclosed = 1
		#self.halcomp.exit()
		gtk.main_quit()
		return False
		
	def on_unix_signal(self,signum,stack_frame):
		print "on_unix_signal(): signal %d received, saving state" % (signum)
		self.ini.save_state(self)
		gtk.main_quit()
		self.halcomp.exit()

	def on_hal_led1_hal_pin_changed(self,hal_led1,data=None):
		n = 0
		#while True:
		#ser = serial.Serial('/dev/ttyUSB0')
		#ser.open()
	 	#ser.write('1')
	 	#print ser.read(2)
	 	#ser.close()
	     
	def on_hal_button1_pressed(self,hal_button1,data=None):
		#n = 0
		#print "%f" % n
		#nhits += 1 
		#hal_label1.set_label("hits: %d" % nhits)
		hal_button1.set_label("aha!")
		
		#ser = serial.Serial('/dev/ttyUSB0')
		#ser.open()
		#while ser.read(1):	
		#	ser.write('1')
		#ser.close()
		
		#ser = serial.Serial('/dev/ttyUSB0')
		#ser.open()
		#ser.write('ACK')
		#print ser.read(2)
		
 		#n = ser.read(2)
 		#print n
 		#if n == 5:
		#	ser.write('ACK')
		#	print ser.read(13)
		
		#ser.close()
    
def get_handlers(halcomp,builder,useropts):
	for cmd in useropts:
		exec cmd in globals()
	return [HandlerClass(halcomp,builder,useropts)]
	
thread1 = myThread(1,"Modbus-Thread-1",0.5)
#thread1.daemon = True;
thread1.start()

if __name__ == "__main__":
  main = main()
  gtk.main()

print "Exiting Main Thread"
Last edit: 10 Jan 2015 00:46 by tnl.lambert.

Please Log in or Create an account to join the conversation.

More
10 Jan 2015 09:55 #54851 by cmorley
The gladevcp error is typical and nothing to worry about. caused by the remapped window (embedded glade panel).

You have loaded classicladder with modbus turned on.
Are you using classicladder with modbus?

Looks like the classicladder realtime module ends before the userland program, causing an error message.
Also I think typical and nothing to worry about.

Chris M

Please Log in or Create an account to join the conversation.

More
12 Jan 2015 23:25 #54939 by tnl.lambert
If both of those errors are typical - how do I fix the GdkWindow warning? And what is keeping the gladevcp process open?

I am currently killing the process in order to stop it, which isn't really a solution. My feeling is that it stems from either the multithreading or the exit handler - but no progress so far. I just need it to terminate fully.

Please Log in or Create an account to join the conversation.

More
13 Jan 2015 05:03 #54945 by cmorley
Why do you care about the warning?
Is there an actual problem or do you just not like the error message?

Please Log in or Create an account to join the conversation.

More
13 Jan 2015 05:13 #54947 by cmorley
The GDK warning is also because of the gladevcp embedded window.

Please Log in or Create an account to join the conversation.

More
21 Jan 2015 23:23 #55240 by tnl.lambert
Yes, there is an actual problem. I'm still avoiding having to deal with it by terminating the process through the 'System Monitor' tool.

I need to understand how the GladeVCP embedded window is terminated when the axis GUI is closed. The essential problem is that unless I make my thread a daemon thread, it won't exit when axis does. Thus, gladevcp runs in the background until I kill it from the System Monitor. I've tried several methods, but none of them have succeeded (the daemon thread method causes several new problems too).

Should I use a signal handler? A join() function? What information does axis pass to GladeVCP when it closes? And what is the proper way for me to end a thread using a GladeVCP signal?

I need axis to tell GladeVCP to stop, so that I can stop my thread at an appropriate point.

Thanks!

Please Log in or Create an account to join the conversation.

More
22 Jan 2015 02:27 - 22 Jan 2015 02:29 #55248 by newbynobi
Hallo,

within the application that call the dynamic tab, you will have to check the socket and remember it. When the main application finish, you have to check if there are still clients open and send to all of them a close terminate signal.

Please check the gmoccapy.py code (~/linuxcnc-dev/src/usr-intf/gmoccapy/gmoccapy.py) from line 919 to line 965 for details.
# =============================================================
# Dynamic tabs handling Start

    def _init_dynamic_tabs( self ):
        # dynamic tabs setup
        self._dynamic_childs = {}
        # register all tabs, so they will be closed together with the GUI
        atexit.register( self._kill_dynamic_childs )

        tab_names, tab_location, tab_cmd = self.get_ini_info.get_embedded_tabs()
        if not tab_names:
            print ( _( "**** GMOCCAPY INFO ****" ) )
            print ( _( "**** Invalid embeded tab configuration ****" ) )
            print ( _( "**** No tabs will be added! ****" ) )
            return

        try:
            for t, c, name in zip( tab_names, tab_cmd, tab_location ):
                nb = self.widgets[name]
                xid = self._dynamic_tab( nb, t )
                if not xid: continue
                cmd = c.replace( '{XID}', str( xid ) )
                child = subprocess.Popen( cmd.split() )
                self._dynamic_childs[xid] = child
                nb.show_all()
        except:
            print( _( "ERROR, trying to initialize the user tabs or panaels, check for typos" ) )

    # adds the embedded object to a notebook tab or box
    def _dynamic_tab( self, widget, text ):
        s = gtk.Socket()
        try:
            widget.append_page( s, gtk.Label( " " + text + " " ) )
        except:
            try:
                widget.pack_end( s, True, True, 0 )
            except:
                return None
        return s.get_id()

    # Gotta kill the embedded processes when gmoccapy closes
    def _kill_dynamic_childs( self ):
        for child in self._dynamic_childs.values():
            child.terminate()

# Dynamic tabs handling End
# =============================================================

Normally that is done by axis, gscreen and gmoccapy by default.

Norbert
Last edit: 22 Jan 2015 02:29 by newbynobi.

Please Log in or Create an account to join the conversation.

More
22 Jan 2015 09:07 #55256 by tnl.lambert
Norbert - Thanks for the reply!

Unfortunately, I'm not sure what you mean. I'm using an embedded window, not a tab, and I am not using gmoccapy. I think I just don't know how to pass the terminate signal to the exit handler. Are you suggesting that I need to modify the dynamic tab segment of Axis itself?

I am also encountering a semi-related problem with the startup procedure. When I call my .ini file, it calls toplevel.py, which imports several other modules, all of which have 'import hal, h = hal.component("component1")' or some variation thereof. The issue is that only the first imported file seems to be able to create new hal components - all other files drop errors saying that there are duplicate modules with the same name. With some simple print statements, I can see that each hal.component call is indeed being executed multiple times. Is there a good reason for this? Perhaps I have factored my code improperly?

Thanks.

Please Log in or Create an account to join the conversation.

Moderators: HansU
Time to create page: 0.092 seconds
Powered by Kunena Forum