source: trunk/src/testing/bin/sageProxy/SAGEGate.py @ 4

Revision 4, 15.9 KB checked in by ajaworski, 13 years ago (diff)

Added modified SAGE sources

Line 
1############################################################################
2#
3# Copyright (C) 2005 Electronic Visualization Laboratory,
4# University of Illinois at Chicago
5#
6# All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11#  * Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13#  * Redistributions in binary form must reproduce the above
14#    copyright notice, this list of conditions and the following disclaimer
15#    in the documentation and/or other materials provided with the distribution.
16#  * Neither the name of the University of Illinois at Chicago nor
17#    the names of its contributors may be used to endorse or promote
18#    products derived from this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#
32# Direct questions, comments etc about SAGE UI to www.evl.uic.edu/cavern/forum
33#
34# Author: Ratko Jagodic
35#       
36############################################################################
37
38
39
40
41
42"""
43        SAGE UI --> SAGE
44        ---------------------------     
45        Register                        1000
46        Execute                         1001
47        Shutdown                        1002
48        Move                            1003
49        Resize                          1004
50        Start Performance               1005
51        Stop Performance                1006
52        BackColor                       1007
53        Z Value Change                  1008
54        Bring To Front                  1010
55        App Properties Change           1011
56        App Frame Rate Change           1012
57        SAGE Shutdown                   1100
58       
59
60        SAGE ----> SAGE UI
61        --------------------------
62        Status                          40000
63        App Info                        40001 APPID L R T B SAIL-ID
64        Performance                     40002
65        App Shutdown                    40003
66        Display Info                    40004
67        Z Order Change                  40005
68       
69
70        STORE INFORMATION
71"""
72
73
74
75from threading import Thread
76import socket, sys, string, os.path, xmlrpclib
77
78
79TIMEOUT_INTERVAL = 2
80BLANK = ' '
81HEADER_ITEM_LEN = 8
82APP_LAUNCHER_PORT = 19010
83
84
85class SAGEGate:
86
87        def __init__(self, logFunc ):
88                self.hashCallbackFunction = {}    # (AKS 2004-10-16)
89                self.threadkilled = False
90                self.connected = False
91                self.WriteLog = logFunc  #redirect output
92       
93                # used for printing out informative messages (on sending and receiving)
94                self.hashOutgoingMessages = {}  #RJ 2005-01-24
95                self.hashOutgoingMessages[1000] = "Register UI"
96                self.hashOutgoingMessages[1001] = "App Start"
97                self.hashOutgoingMessages[1002] = "App Shutdown"
98                self.hashOutgoingMessages[1003] = "Move"
99                self.hashOutgoingMessages[1004] = "Resize"
100                self.hashOutgoingMessages[1005] = "Request Performance"
101                self.hashOutgoingMessages[1006] = "Stop Performance"
102                self.hashOutgoingMessages[1007] = "Background Color"
103                self.hashOutgoingMessages[1008] = "Z Value"
104                self.hashOutgoingMessages[1010] = "Bring To Front"
105                self.hashOutgoingMessages[1011] = "App Properties Change"
106                self.hashOutgoingMessages[1012] = "App Frame Rate Change"
107                self.hashOutgoingMessages[1015] = "Stream Request"
108                self.hashOutgoingMessages[1100] = "Shutdown"
109
110                self.hashIncomingMessages = {} #RJ 2005-01-24
111                self.hashIncomingMessages[40000] = "SAGE Status Message"
112                self.hashIncomingMessages[40001] = "App Info Return"
113                self.hashIncomingMessages[40002] = "Performance Info"
114                self.hashIncomingMessages[40003] = "App Shutdown"
115                self.hashIncomingMessages[40004] = "Display Info"
116                self.hashIncomingMessages[40005] = "Z Change"
117               
118
119                                       
120        def makemsg(self,dst,code,appcode,size,data):
121                msg = '%8s\0%8s\0%8s\0%s\0' %(dst,code,appcode,data)
122                size = len(msg) + 9
123                msg = '%8s\0%s' %(size,msg)
124                return msg     
125
126
127        def connectToAppLauncher(self, host=socket.gethostname(), port=APP_LAUNCHER_PORT):
128                self.appLauncher = xmlrpclib.ServerProxy("http://" + host + ":" + str(port))
129                try:
130                        self.getAppList()
131                except socket.error:
132                        return False
133                except:
134                        pass
135                return True
136               
137       
138        ##################################################################     
139        #  Connect To Sage
140        ##################################################################     
141        def connectToSage(self,host=socket.gethostname(),port=20001):
142                if self.connected == True: return 0
143
144                ## connect to the app launcher first
145                if not self.connectToAppLauncher(host, APP_LAUNCHER_PORT):
146                        self.WriteLog("\n===> Can't connect to the appLauncher")
147                        return -1
148               
149                ## create socket
150                self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
151                if self.sock is None: return 0
152
153                if host: self.sageHost = host
154                if port > 0: self.sagePort = port
155                ## connect
156                try:
157                        self.sock.connect((host,port))
158                except socket.error:
159                        self.WriteLog('can\'t connect to SAGE on '+ self.sageHost + ":" + str(self.sagePort))
160                        return 0
161
162                #### RJ 2005-01-21
163                # without this, this thread will never exit because sock.recv is blocking and it will wait until
164                # something comes in. What happens is, "while" checks if threadkilled is false, it is so it goes
165                # in here and blocks until something is received. In the meantime, we want to quit so we set
166                # threadkilled to True but since we are blocked by sock.recv, we wont be able to get to the "while" again
167                self.sock.settimeout(TIMEOUT_INTERVAL) 
168               
169                ##### FOR Thread
170                self.t = Thread(target=self.recvWorker,args=())
171                self.t.setDaemon(True)
172                self.t.start()
173
174                self.threadkilled = False
175                self.connected = True
176
177                #print 'connected to SAGE',self.sageHost,self.sagePort
178                return 1
179       
180        def disconnectFromSage(self):
181                if self.connected == False: return 0
182               
183                self.threadkilled = True
184                self.connected = False
185               
186                self.t.join()
187                self.sock.close()
188                del self.sock
189                #print 'disconnected from SAGE',self.sageHost,self.sagePort
190
191                return 1
192        #################################################################
193
194        def sendmsg(self,msg):
195                totalcount = 0
196                try:
197                        totalcount = self.sock.send(msg)
198                except socket.error:
199                        #print 'socket error'
200                        totalcount = -1
201                except Exception:
202                        totalcount = -1
203                return totalcount
204
205
206        # get the applist from the applauncher first and call the appropriate function
207        def getAppList(self):
208                try:
209                        appList = self.appLauncher.getAppList()
210                except socket.error:
211                        return -1  #the server is not running
212                else:
213                        self.hashCallbackFunction[ 40000 ]( appList )
214                       
215
216
217        ##################################################################     
218        # Register
219        ##################################################################     
220        # 1000 none
221        # #############40000 SAGE-STATUS Format (?)
222        # 40004 display info format
223        # 40001 app-inst-ID left right top bottom sail-ID
224        ##################################################################     
225        def registerSage(self):
226                if self.connected == False:
227                        return 0
228                msg = self.makemsg('',1000,'',0,'')
229                status = self.sendmsg(msg)
230                return status
231
232        ##################################################################     
233        # Execute
234        ##################################################################     
235        # 1001 app-name 100 100 (app-name(?))
236        # 40001 app-inst-ID left right top bottom sail-ID
237        ##################################################################     
238        def executeApp(self, appName, configName="default", pos=False, size=False, shareable=False, optionalArgs=""):
239                if self.connected == False: return 0
240                if not appName: return 0
241
242                return self.appLauncher.startDefaultApp(appName, self.sageHost, self.sagePort+1, shareable, configName, pos, size, optionalArgs)
243
244               
245
246
247        ##################################################################     
248        # Shutdown
249        ##################################################################     
250        # 1002 app-instance
251        # 40003 app-Inst-ID (?)
252        ##################################################################     
253        def shutdownApp(self,appId):
254                if self.connected == False: return 0
255                #if not appId: return 0
256
257                #msg = self.makemsg('',1002,'',len(appId),appId)
258                data = "" + str(appId)
259                msg = self.makemsg('',1002,'',len(data),data)
260                status = self.sendmsg(msg)
261                return status
262               
263        ##################################################################     
264        # Move
265        ##################################################################     
266        # 1003 app-instance dist-X,dist-Y
267        ##################################################################     
268        def moveWindow(self,appId,distX,distY):
269                if self.connected == False: return 0
270
271                #make sure all the coordinates are ints
272                distX = int(distX)
273                distY = int(distY)
274               
275                data = str(appId) + BLANK + str(distX) + BLANK + str(distY)
276                msg = self.makemsg('',1003,'',len(data),data)
277                status = self.sendmsg(msg)
278                return status
279       
280        ##############################################################
281        # Resize
282        # 1004 app-instance lef,right,top,bottom
283        ##################################################################
284        def resizeWindow(self,appId,left=0,right=0,bottom=0,top=0):
285                if self.connected == False: return 0
286                #if not appId: return 0
287
288                #make sure all the coordinates are ints
289                left = int(left)
290                right = int(right)
291                bottom = int(bottom)
292                top = int(top)
293
294                data = str(appId) + BLANK + str(left) + BLANK + str(right) + BLANK + str(bottom) + BLANK + str(top)
295                msg = self.makemsg('',1004,'',len(data),data)
296                status = self.sendmsg(msg)
297                return status
298
299        ###########################################################
300        # Performance Information
301        ###########################################################
302        # 1005 app-instance sending-rate
303        # 1006 app-instance     
304        ###########################################################     
305        def startPerformance(self,appId,sendingrate=2):
306                if self.connected == False: return 0
307                data = "%d %d" % (appId, sendingrate )
308                msg = self.makemsg('',1005,'',len(data),data)
309                status = self.sendmsg(msg)     
310                return status
311       
312        def stopPerformance(self,appId):
313                if self.connected == False: return 0
314                appId = str(appId) # convert the data to string format
315               
316                msg = self.makemsg('',1006,'',len(appId),appId)
317                status = self.sendmsg(msg)
318                return status
319
320
321        ####################################   
322        # Background Color
323        # 1007 red,green blue   
324        ##################################################################
325        def setBgColor(self,(red,green,blue)=(1,1,1)):
326                if self.connected == False: return 0
327
328                data = str(red) + BLANK + str(green) + BLANK + str(blue)
329                msg = self.makemsg('',1007,'',len(data),data)
330                status = self.sendmsg(msg)
331                return status
332
333
334        ####################################   
335        # bring the application window to the top (front)
336        # 1010 app-inst-ID
337        ##################################################################
338        def bringToFront(self, appId):
339                if self.connected == False: return 0
340
341                data = str(appId)
342                msg = self.makemsg('',1010,'',len(data),data)
343                status = self.sendmsg(msg)
344                return status
345
346
347        ####################################   
348        # Change App Properties
349        # 1011 appId, fsmIP, fsmPort, appConfigNum     
350        ##################################################################
351        def changeAppProperties(self,appId, newTitle, newTitleColor=(-1,-1,-1), newBorderColor=(-1,-1,-1)):
352                if self.connected == False: return 0
353
354                data = str(appId) + BLANK + str(newTitle)
355                data = data + BLANK + str(newTitleColor[0]) + BLANK + str(newTitleColor[1]) + BLANK + str(newTitleColor[2])
356                data = data + BLANK + str(newBorderColor[0]) + BLANK + str(newBorderColor[1]) + BLANK + str(newBorderColor[2])
357                #data = data + BLANK + str(newTitle)
358                msg = self.makemsg('',1011,'',len(data),data)
359                status = self.sendmsg(msg)
360                return status
361
362        ####################################   
363        # Change App Frame Rate
364        # 1012 appId, fsmIP, fsmPort, appConfigNum     
365        ##################################################################
366        def changeAppFrameRate(self,appId, newFrameRate):
367                if self.connected == False: return 0
368
369                data = str(appId) + BLANK + str(newFrameRate)
370                msg = self.makemsg('',1012,'',len(data),data)
371                status = self.sendmsg(msg)
372                return status
373
374
375        ####################################   
376        # Stream Request
377        # 1015 appId, fsmIP, fsmPort
378        ##################################################################
379        def streamApp(self,appId, fsmIP, fsmPort):
380                if self.connected == False: return 0
381
382                data = str(appId) + BLANK + str(fsmIP) + BLANK + str(fsmPort)
383                msg = self.makemsg('',1015,'',len(data),data)
384                status = self.sendmsg(msg)
385                return status
386       
387
388        ####################################   
389        # SAGE shutdown
390        # 1100  <none>
391        ##################################################################
392        def shutdownSAGE(self):
393                if self.connected == False: return 0
394                msg = self.makemsg('',1100,'',0,'')
395                status = self.sendmsg(msg)
396                return status
397
398
399        ### informs the user when the connection breaks
400        def showConnectionClosedDialog(self):
401                pass
402
403       
404        ##############
405        ## Recv Worker Thread
406        ##################################################################
407        def recvWorker(self):
408               
409                while self.threadkilled == False:   #doesn't work as expected without the sock.settimeout (look below)
410
411                        #############################
412                        try:
413
414                                code = ""
415                                incomingMsg = ""
416                                msgSize = ""
417                               
418                                # first make sure you read the whole 8 bytes for the size
419                                while len(msgSize) < HEADER_ITEM_LEN:
420                                        msgSize = self.sock.recv(HEADER_ITEM_LEN)
421                                        if len( msgSize ) == 0:
422                                                self.threadkilled = True
423                                                break
424                                       
425                                if self.threadkilled == True:
426                                        #wx.CallAfter(self.showConnectionClosedDialog)
427                                        break
428
429                                # this is the number of bytes that the total message contains
430                                msgSize = msgSize.replace('\x00', '')
431                                sizeLeft = int(msgSize) - HEADER_ITEM_LEN    # we already read the size so read the rest of the bytes
432
433                                # read the rest of the message
434                                while len( incomingMsg ) < sizeLeft:
435                                        incomingMsg = incomingMsg + self.sock.recv( sizeLeft - len(incomingMsg) )
436
437                               
438                                # extract the tokens from the message
439                                if len( incomingMsg ) > 0:
440                                        incomingMsg = incomingMsg.replace('\x00', ' ')
441                                        dst = incomingMsg[ 1:9 ].strip()
442                                        code = incomingMsg[ 10:18 ].strip()
443                                        appCode = incomingMsg[ 19:27 ].strip()
444                                        data = incomingMsg[ 28: ].strip()
445
446                                        # print the message out
447                                        if int(code) in self.hashIncomingMessages and int(code) != 40002:
448                                                self.WriteLog("\n\tRECEIVED:  " + self.hashIncomingMessages[int(code)])
449                                                lines = data.split('\n')
450                                                if len(lines) < 2:
451                                                        self.WriteLog("\t\t   [" + lines[0] + "]\n\n")
452                                                else:
453                                                        for i in range(0, len(lines)):
454                                                                if i == 0:
455                                                                        self.WriteLog("\t\t   [" + lines[i])
456                                                                elif i == len(lines)-1:
457                                                                        self.WriteLog("\t\t    " + lines[i] + "]\n\n")
458                                                                else:
459                                                                        self.WriteLog("\t\t    " + lines[i])
460
461                                   
462                        except socket.timeout:
463                                continue
464                        except socket.error:
465                                #print 'socket error on receive'
466                                continue
467                        except:
468                                self.WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
469                                continue
470               
471
472                        ## SAGE Status
473                        if self.threadkilled:
474                            break
475
476                        # now call the appropriate function to update the datastructure
477                        if self.hashCallbackFunction.has_key( int(code) ):
478                            self.hashCallbackFunction[ int(code) ]( data )
479
480               
481
482
483        # converts all non-printable characters from the buffer to white spaces
484        # (so that they can be removed using string.strip() function)
485        def cleanBuffer( self, stBuffer ):
486            stNewBuffer = ""
487           
488            for ch in stBuffer:
489                if ( ch in string.printable ):
490                    stNewBuffer = stNewBuffer + ch
491                else:
492                    stNewBuffer = stNewBuffer + " "
493           
494            return stNewBuffer
495
496
497
498
499        def registerCallbackFunction( self, msgID, function ):
500            self.hashCallbackFunction[ msgID ] = function
501               
502
Note: See TracBrowser for help on using the repository browser.