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

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

Added modified SAGE sources

Line 
1#!/usr/bin/python
2
3############################################################################
4#
5# Copyright (C) 2005 Electronic Visualization Laboratory,
6# University of Illinois at Chicago
7#
8# All rights reserved.
9#
10# Redistribution and use in source and binary forms, with or without
11# modification, are permitted provided that the following conditions are met:
12#
13#  * Redistributions of source code must retain the above copyright
14#    notice, this list of conditions and the following disclaimer.
15#  * Redistributions in binary form must reproduce the above
16#    copyright notice, this list of conditions and the following disclaimer
17#    in the documentation and/or other materials provided with the distribution.
18#  * Neither the name of the University of Illinois at Chicago nor
19#    the names of its contributors may be used to endorse or promote
20#    products derived from this software without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
26# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33#
34# Direct questions, comments etc about SAGE UI to www.evl.uic.edu/cavern/forum
35#
36# Author: Ratko Jagodic
37#       
38############################################################################
39
40
41from SimpleXMLRPCServer import *
42from time import sleep
43import socket, sys, string, os.path, SocketServer, base64, time, optparse
44from sageUIDataInfo import *
45from SAGEGate import *
46import traceback as tb
47from threading import RLock
48
49XMLRPC_PORT = 20001 #9192
50REDIRECT = True
51
52
53# to output all the error messages to a file
54outputLock = RLock()
55def WriteLog(message):
56    outputLock.acquire()
57    try:
58        if not REDIRECT:
59            #tb.print_exc()
60            print message
61        else:
62            f = open("sage_proxy.log", "a")
63            #tb.print_exc(file=f)
64            f.write(message+"\n")
65            f.close()
66    except:
67        pass
68    outputLock.release()
69
70
71
72
73
74# a mix-in class for adding the threading support to the XMLRPC server
75class ThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
76    allow_reuse_address = True
77
78
79class Proxy:
80    def __init__(self, host=socket.gethostname(), port=20001, passwd=""):
81        self.sageData = sageUIDataInfo()   # a datastructure that stores the display and app state
82       
83        self.sageGate = SAGEGate(WriteLog)         # communication channel with sage
84        self.sageGate.registerCallbackFunction( 40000, self.sageData.setSageStatus )
85        self.sageGate.registerCallbackFunction( 40001, self.sageData.setSageAppInfo )
86        self.sageGate.registerCallbackFunction( 40003, self.sageData.sageAppShutDown )
87        self.sageGate.registerCallbackFunction( 40004, self.sageData.setSageDisplayInformation )
88        self.sageGate.registerCallbackFunction( 40005, self.sageData.setSageZValue )
89
90        # now connect to sage and register with it
91        if self.sageGate.connectToSage(host, port) == 0:
92            sys.exit(0)
93        self.sageGate.registerSage()
94        self.sessionPassword = passwd
95
96
97    def getDisplayInfo(self):
98        """ Returns: a list (integers): [totalNumTiles, numTilesX, numTilesY, totalWidth,
99                                         totalHeight, tileWidth, tileHeight]
100            Returns: -1 if failed for whatever reason
101        """
102        try:
103            return self.sageData.getSageDisplayInformation()
104        except:
105            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
106            return -1
107
108
109    def getAppList(self):
110        """ Returns: a list (strings) of app names available for running
111            Returns: -1 if failed for whatever reason
112        """
113        try:
114            return self.sageData.getAvailableAppNames()
115        except:
116            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
117            return -1
118
119
120    def getZValues(self):
121        """ Returns: a list (integers) of z values [numChanges, appId, zValue, appId, zValue ....]
122            Returns: -1 if failed for whatever reason
123        """
124        try:
125            return self.sageData.getZvalues()
126        except:
127            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
128            return -1
129
130
131    def getAppStatus(self, appId=-1):
132        """ If called without parameters it will return the status for all currently running applications.
133            If called with appId parameter it will return the status just for that application.
134            Returns: a hash where the key is appId (string) and the value is a list of app status:
135                    [string appName, int appId, int left, int right, int bottom,
136                     int top, int sailID, int zValue, int configNum, string title]
137            Returns: -1 if failed for whatever reason
138             
139        """
140        try:
141            if appId == -1:
142                return self.sageData.getAllAppInfo()
143            else:
144                return self.sageData.getAppInfo(appId)
145        except:
146            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
147            return -1
148
149
150    def getAllAppID(self):
151        """ Returns all the app ID currently running on SAGE
152        """
153        try:
154            return self.sageData.getAllAppIDs()
155        except:
156            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
157            return -1
158
159
160    def getNewAppID(self):
161        try:
162           return self.sageData.getNewAppID()
163        except:
164           WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
165           return -1
166
167
168    def bringToFront(self, appId):
169        """ Brings the application window to front (on top of the other ones).
170            Returns: 1 if everythin went OK and the message was sent
171            Returns: -1 if the message was not sent for whatever reason (including
172                     if the app was already on top)
173        """
174        try:
175                if self.sageData.appExists(appId) and \
176                   (not self.sageData.isTopWindow(appId)) and \
177                   self.sageGate.bringToFront(appId):
178                    return 1
179                else:
180                    return -1
181        except:
182            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
183            return -1
184       
185
186    def moveWindow(self, appId, x, y):
187        """ Moves a window to the specified location (absolute position) in tiled display coordinates.
188            The x,y is interpreted as the lower left corner of the application.
189            All parameters are integers.
190            Returns: 1 if successful.
191            Returns: -1 if failed for whatever reason.
192        """
193        try:
194            app = self.sageData.getSAGEApp(appId)
195            left = x
196            right = x+app.getWidth()
197            bottom = y
198            top = y+app.getHeight()
199            if self.sageGate.resizeWindow(appId, left, right, bottom, top) == -1:
200                return -1
201            return 1
202        except:
203            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
204            return -1
205
206
207    def resizeWindow(self, appId, left, right, bottom, top):
208        """ Resizes a window to the specified size in tiled display coordinates.
209            The lower left corner of the display is considered 0,0.
210            All parameters are integers.
211            Returns: 1 if successful.
212            Returns: -1 if failed for whatever reason.
213        """
214        try:
215           
216            # prevent 0 or negative size
217            if (right-left < 1) or (top-bottom < 1):
218                app = self.sageData.getSAGEApp(appId)
219                ar = app.getWidth() / float(app.getHeight())
220                if ar > 1:
221                    newH = 300 # min app size
222                    newW = newH*ar
223                else:
224                    newW = 300 # min app size
225                    newH = newW/ar
226               
227                right = left+newW
228                top = bottom+newH
229
230            if self.sageGate.resizeWindow(appId, left, right, bottom, top) == -1:
231                return -1
232            return 1
233        except:
234            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
235            return -1
236
237
238    def executeApp(self, appName, configName="default", pos=False, size=False, shareable=False, optionalArgs=""):
239        """ Starts a new application with appName (string) and configNum (int). You can also optionally specify
240            initial position and size of the application window that you pass in as a tuple of integers.
241            If the application itself requires some command line arguments during startup, those can be
242            specified as well with the optionalArgs parameter (string).
243            Shareable parameter is used when you want to run the application through sageBridge which
244            means that it can be shared among other displays. If False it will run the app locally.
245            Returns: the new status of all the apps in the same format as getAppStatus
246                    (sleeps 2 seconds before returning it to ensure that the datastructure was updated
247                     by the messages from SAGE about the new app)
248            Returns: -1 if failed for whatever reason
249        """
250        try:
251            if self.sageGate.executeApp(appName, configName, pos, size, shareable, optionalArgs) == -1:
252                return -1
253            sleep(2)        #wait for the message to arrive from sage and then return the status
254            return self.sageData.getAllAppInfo()
255        except:
256            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
257            return -1
258
259
260    def closeApp(self, appId):
261        """ Closes the app corresponding to the specified appId.
262            Returns: the new status of all the apps in the same format as getAppStatus
263                    (sleeps 2 seconds before returning it to ensure that the datastructure was updated
264                     by the messages from SAGE about the closed app)
265            Returns: -1 if failed for whatever reason
266        """
267        try:
268            if not self.sageData.appExists(appId):
269                return -1
270           
271            if self.sageGate.shutdownApp(appId) == -1:
272                return -1
273            sleep(2)        #wait for the message to arrive from sage and then return the status
274            return self.sageData.getAllAppInfo()
275        except:
276            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
277            return -1
278       
279
280    def shareDesktop(self, sz, displayNum, ip, passwd, shareable=False):
281        oa = ip +" "+ str(displayNum) +" "+ str(sz[0]) +" "+ str(sz[1]) +" "+ passwd
282        return self.executeApp("VNCViewer", "default", False, sz, shareable, oa)
283
284
285    def shareApp(self, appId, fsIP, fsPort):
286        """ Sends a request to fsManager to share the specified application to the specified display
287            Parameters: - appId of the application you want to share
288                        - ip address and port number of the machine to stream to
289            Returns: 1 if succeeded
290            Returns: -1 if failed for whatever reason
291        """
292        try:
293            if self.sageGate.streamApp(appId, fsIP, fsPort) == -1:
294                return -1
295            else:
296                return 1
297        except:
298            WriteLog( str(sys.exc_info()[0])+" "+str(sys.exc_info()[1]) )
299            return -1
300
301       
302    def authenticate(self, passwd):
303        if (self.sessionPassword == passwd):
304           return 1
305        else:
306           return -1   
307
308
309
310
311### sets up the parser for the command line options
312def get_commandline_options():
313    parser = optparse.OptionParser()
314
315    h = "if set, prints output to console, otherwise to sage_proxy.log"
316    parser.add_option("-v", "--verbose", action="store_true", help=h, dest="verbose", default=False)
317
318    h = "which machine is sage running on (default is localhost)"
319    parser.add_option("-s", "--server", dest="server", help=h, default="localhost")
320
321    h = "the port number where sage is accepting ui connections (default is 20001)"
322    parser.add_option("-p", "--port", help=h, type="int", dest="port", default=20001)
323
324    h = "specify the password for SAGE Web UI access (default is empty)"
325    parser.add_option("-x", "--pass", dest="passwd", help=h, default="")
326
327    return parser.parse_args()
328
329
330
331def main(argv):
332    global REDIRECT
333   
334    (options, args) = get_commandline_options()
335    REDIRECT = not options.verbose
336    port = options.port
337    host = options.server
338    passwd = options.passwd
339   
340    p = Proxy(host, port, passwd)
341
342    # always use the fsManager ui port + 3 for the xmlrpc port
343    global XMLRPC_PORT
344    XMLRPC_PORT = port+3
345   
346    server = ThreadedXMLRPCServer(("", XMLRPC_PORT), SimpleXMLRPCRequestHandler, False)
347    server.allow_reuse_address = True
348    server.register_instance(p)
349    server.register_introspection_functions()
350    server.serve_forever()
351       
352
353
354if __name__ == '__main__':
355    import sys, os
356    main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
Note: See TracBrowser for help on using the repository browser.