1 # **** Import Section ****
2 #
3 # This section includes all the import files needed for the script.
4 # The first set of imports are jython specific, the second set are
5 # java/nemo specific (but the order is not important)
6
7 from time import *
8 from string import *
9
10 from nemo.pdi import *
11 from nemo.experiment import GenericDataPoint
12
13
14
15
16 # Device interface objects
17 buttonBox = 0
18 display = 0
19
20
21
22 # The data type
23 dt = 0
24 dataSet = None
25
26
27
28
29 # This is a class. It contains variables and methods for a common purpose.
30 # This particualr class, called ExampleClass, has methods for displaying
31 # various types of Graphics and for logging data.
32 class ExampleClass:
33     
34     # The __init__ method is called when the class is instatiated (when
35     # an instance of this class is created, as in:
36     # myClass = ExampleClass()
37     # This is a good place to initialize any variables necessary.
38     #
39     # The first argument for each method in a class should be called 'self'.
40     # 'self' is basically a self-reference, referring to this instance of
41     # a class.
42     def __init__(self, aDisplayHandle, aDataType):
43         self.displayHandle = aDisplayHandle
44         self.dataType = aDataType
45         
46         # Create several graphics for later use and store them in class
47         # variables.
48         self.rectangle1 = self.createRectangle(40, 35, 20, 30)
49         self.rectangle1.name = self.rectangle1.name + '-1'
50         self.rectangle1Showing = 0
51         
52         self.rectangle2 = self.createRectangle(62, 35, 10, 20)
53         self.rectangle2.name = self.rectangle2.name + '-2'
54         self.rectangle2Showing = 0
55         
56         self.circle = self.createCircle()
57         self.circleShowing = 0
58         
59         self.image = self.createImage()
60         self.imageShowing = 0
61         
62         self.line = self.createLine()
63         self.lineShowing = 0
64         
65         self.text = self.createText()
66         
67         # This variable will be true when the task is running.
68         self.running = 0
69     
70     # Create a Rectangle Graphic using the given (x,y) position and (w,h) dimensions
71     # and then return it to the caller.
72     def createRectangle(self, x, y, w, h):
73         rectangle = GraphicFactory.createGraphic(GraphicFactory.FILL_RECTANGLE)
74         
75         rectangle.name = 'rectangle example'
76         rectangle.description = 'a solid red rectangle'
77         
78         # Specify the RGB components of the color (red).
79         rectangle.red = 255
80         rectangle.green = 0
81         rectangle.blue = 0
82         
83         # Specify the position and size.
84         rectangle.x = str(x)
85         rectangle.y = str(y)
86         rectangle.w = str(w)
87         rectangle.h = str(h)
88         
89         # We can return any variable from a method, without having to specify
90         # a return type in the method sgnature. Here we return the rectangle
91         # Graphic.
92         return rectangle
93     
94     # Create a Circle Graphic and then return it to the caller.
95     def createCircle(self):
96         circle = GraphicFactory.createGraphic(GraphicFactory.FILL_CIRCLE)
97         
98         circle.name = 'circle example'
99         circle.description = 'a solid blue circle'
100         
101         # Specify the RGB components of the color (blue).
102         circle.red = 0
103         circle.green = 0
104         circle.blue = 255
105         
106         # Specify the position and diameter.
107         circle.x = '45'
108         circle.y = '0'
109         circle.diameter = '10'
110         
111         # Here we return the Circle Graphic.
112         return circle
113     
114     # Create a Text Graphic and then return it to the caller.
115     def createText(self):
116         text = GraphicFactory.createGraphic(GraphicFactory.DRAW_STRING)
117         
118         text.name = 'text example'
119         text.description = 'sample text'
120         
121         # Specify the RGB components of the color (white).
122         text.red = 255
123         text.green = 255
124         text.blue = 255
125         text.alpha = 50 # This is for transparency
126         
127         # Specify the position.
128         text.x = '5'
129         text.y = '88'
130         
131         # Specify the Font name. Note that the font you specify is not guaranteed
132         # to exist on the machine that runs the task.
133         text.fontName = 'Arial'
134         
135         # Text can have the following styles: NORMAL (default), BOLD, ITALIC. Create
136         # combinations by using the bitwise 'OR' of two or more constants together.
137         text.fontStyle = text.BOLD | text.ITALIC
138         
139         # Specify a bounding box for the text. The text is guaranteed to be sized
140         # such that it will fit into the bounding box.
141         text.fontWidth = '90'
142         text.fontHeight = '10'
143         
144         # The font will not be stretched to fit the boudning box exactly, so more
145         # than likely there will be padding. By default, the padding is measured
146         # so that the text will be centered within the boudning box. However,
147         # you can specify the alignment of the text: ALIGN_LEFT, ALIGN_RIGHT, and
148         # ALIGN_CENTER (default).
149         text.alignment = text.ALIGN_CENTER
150         
151         # Specify the actual text to appear in the Graphic.
152         text.text = 'enter graphic key or Q to quit'
153         
154         # Here we return the Text Graphic.
155         return text
156     
157     # Create a Line Graphic and then return it to the caller.
158     def createLine(self):
159         line = GraphicFactory.createGraphic(GraphicFactory.DRAW_LINE)
160         
161         line.name = 'line example'
162         line.description = 'an orange line'
163         
164         # Specify the RGB components of the color (orange).
165         line.red = 255
166         line.green = 128
167         line.blue = 0
168         
169         # Specify the positions of the endpoints of the line.
170         line.x1 = '32'
171         line.y1 = '17'
172         line.x2 = '25'
173         line.y2 = '17'
174         
175         # Here we return the Line Graphic.
176         return line
177     
178     # Create an image Graphic and then return it to the caller.
179     def createImage(self):
180         image = GraphicFactory.createGraphic(GraphicFactory.DRAW_IMAGE)
181         
182         image.name = 'image example'
183         image.description = 'a self-contained image'
184         
185         # Specify the position.
186         image.x = '75'
187         image.y = '75'
188         
189         # Specify a bounding box for the size of the image. If no width
190         # or height is specified, the native image size is used. This
191         # bounding box works like the text Graphic bounding box. The
192         # aspect ratio is locked, so the image retains proper proportions.
193         image.w = '25'
194         image.h = '25'
195         
196         # There are two ways to specify the image contents. The first way
197         # stores the image bytes in the Graphic object. We use the task
198         # handle to request the data from NEMO:
199         image.imageBytes = task.getResource('myImage.jpg')
200         
201         # The second way to specify the image contents is to use the resource
202         # architecture. A resource is some file that this script has acces to
203         # on the server. The script can request that the resource be sent to the
204         # client before any Graphic actually needs it. At some point later, a
205         # Graphic object can refer to the resource by name to use it. Assuming
206         # that the script already sent the resource called 'myImage.jpg' to the
207         # cient, this is how an image Graphic uses a resource:
208         image.isResource = 1
209         image.imageName = 'myImage.jpg'
210         
211         # Note that only one of the above two methods needs to be used for a
212         # given image Graphic.
213         
214         # Here we return the image Graphic.
215         return image
216     
217     # This method takes a data point as a paramter and tries to log it to the
218     # data logger defined in this class.
219     def logDataPoint(self, aDataPoint):
220         # Make sure the logger has been initialized before trying to
221         # access it.
222         if (self.dataLogger <> None):
223             self.dataLogger.logDataPoint(aDataPoint)
224     
225     # Set the graphics mode to show. Send a graphic to the client. The Graphic
226     # contains a text instruction.
227     def run(self):
228         # Start a Run, marked by the given name.
229         task.startRun('My Run Name')
230         
231         # Start a Data Logger for the current Run using the given Data Type name
232         # and client. Each client will have its own Data Logger.
233         self.dataLogger = task.startDataSet(self.dataType.getName(), 0)
234         
235         # Set the running mode to 1
236         self.running = 1
237         
238         # Show the graphics for client 0.
239         self.displayHandle.showGraphics(0)
240         
241         # Add a graphic, self.text, to the drawing list for client 0.
242         self.displayHandle.addGraphic(0, self.text)
243     
244     # The task is finished. Close the Data Logger. Stop the run. Stop the experiment.
245     def finish(self):
246         # Set the running mode to 0
247         self.running = 0
248         
249         # Close the Data Logger.
250         task.endDataSet(self.dataLogger)
251         
252         # End the Run.
253         task.endRun()
254         
255         # Set the Data Logger to None (which is equivalent to null). Future
256         # attemps to log data will now fail.
257         self.dataLogger = None
258         
259         # Clear the Graphics for client 0.
260         self.displayHandle.clearGraphics(0)
261         
262         # Pause for 7 seconds.
263         sleep(7)
264         
265         # Stop the experiment. NEMO will now store the data in the database.
266         task.endExperiment()
267     
268     # This method responds to events from the display device. It takes the event
269     # data and logs information about it in a readable form.
270     def handleDisplayEvent(self, anEvent):
271         # The Graphic Event contains variables for:
272         # - the originating client,
273         # - the time at which the event occurred,
274         # - the type of the graphic event, and
275         # - a list of the graphics in the drawing queue.
276         client = anEvent.getClient()
277         aTime = anEvent.getTime()
278         type = anEvent.getType()
279         
280         # Store information about each Graphic into a string summary.
281         graphicsList = ""
282         graphics = anEvent.getGraphics()
283         for graphicData in graphics:
284             graphicsList = graphicsList + graphicData + " "
285         # Remove the trailing white space
286         graphicsList = graphicsList.strip()
287         
288         # Translate the graphic event type, which is an integer value,
289         # into something readable for logging.
290         typeText = ""
291         if (type == GraphicalDisplay.HIDE):
292             typeText = "HIDE"
293         elif (type == GraphicalDisplay.SHOW):
294             typeText = "SHOW"
295         elif (type == GraphicalDisplay.DRAW):
296             typeText = "ADD GRAPHIC"
297         elif (type == GraphicalDisplay.CLEAR_GRAPHICS):
298             typeText = "CLEAR ALL"
299         elif (type == GraphicalDisplay.ADD_GRAPHICS):
300             typeText = "ADD GRAPHICS"
301         elif (type == GraphicalDisplay.CREATE_GROUP):
302             typeText = "ADD GRAPHIC GROUP"
303         
304         # Create a GenericDataPoint instance.
305         gdp = GenericDataPoint()
306         
307         # Set the Data Type
308         gdp.dt = self.dataType
309         
310         # Assign values to the fields of this data type
311         # This data type has fields for the time and a description of what
312         # happened (in string form).
313         gdp.setField("time", str(aTime))
314         gdp.setField("event", str(typeText) + ":{" + graphicsList + "}")
315         
316         # Call the method in this class to log the data point.
317         self.logDataPoint(gdp)
318     
319     # Reponds to a button press from the client.
320     def handleButtonEvent(self, anEvent):
321         # Only listen to button events when we're in running mode 1
322         if (not self.running):
323             return
324         
325         # The Button Event contains variables for:
326         # - the originating client,
327         # - the time at which the key was pressed, and
328         # - the key pressed.
329         client = anEvent.getClient()
330         pressTime = anEvent.getTime()
331         key = anEvent.getData()
332         
333         # If, for some reason, there is no key identified, return without
334         # doing anything.
335         if (len(key) == 0):
336             return
337         
338         # Verify that they button pressed is either a letter, a digit, or some
339         # punctuation character. If it's not, then return without doing anything.
340         if (not key[0] in (letters+digits+'`~!@#$%^&*()-=_+{}[]\\|;:\'\",<.>/?')):
341             return
342         
343         # Based on which key is pressed, take various actions.
344         if (key == 'Q' or key == 'q'):
345             # The Q key i sused when the task is done.
346             self.finish()
347         elif (key == 'C' or key == 'c'):
348             # The C key shows or hides the circle graphic.
349             if (self.circleShowing):
350                 self.displayHandle.removeGraphic(0, self.circle.name)
351             else:
352                 self.displayHandle.addGraphic(0, self.circle)
353             self.circleShowing = not self.circleShowing
354         elif (key == 'L' or key == 'l'):
355             # The L key shows or hides the line graphic.
356             if (self.lineShowing):
357                 self.displayHandle.removeGraphic(0, self.line.name)
358             else:
359                 self.displayHandle.addGraphic(0, self.line)
360             self.lineShowing = not self.lineShowing
361         elif (key == '1'):
362             # The 1 key shows or hides the rectangle1 graphic.
363             if (self.rectangle1Showing):
364                 self.displayHandle.removeGraphic(0, self.rectangle1.name)
365             else:
366                 self.displayHandle.addGraphic(0, self.rectangle1)
367             self.rectangle1Showing = not self.rectangle1Showing
368         elif (key == '2'):
369             # The 2 key shows or hides the rectangle2 graphic.
370             if (self.rectangle2Showing):
371                 self.displayHandle.removeGraphic(0, self.rectangle2.name)
372             else:
373                 self.displayHandle.addGraphic(0, self.rectangle2)
374             self.rectangle2Showing = not self.rectangle2Showing
375         elif (key == 'I' or key == 'i'):
376             # The I key shows or hides the image graphic.
377             if (self.imageShowing):
378                 self.displayHandle.removeGraphic(0, self.image.name)
379             else:
380                 self.displayHandle.addGraphic(0, self.image)
381             self.imageShowing = not self.imageShowing
382         elif (key == '0'):
383             # The 0 key clears all the graphics.
384             self.displayHandle.clearGraphics(0)
385             self.displayHandle.addGraphic(0, self.text)
386             self.lineShowing = 0
387             self.imageShowing = 0
388             self.circleShowing = 0
389             self.rectangle1Showing = 0
390             self.rectangle2Showing = 0
391
392     # This method responds to events from the scanner device. It takes the event
393     # data and logs information about it in a readable form.
394     def handleScannerEvent(self, anEvent):
395         # The Scanner Event contains variables for:
396         # - the originating client,
397         # - the time at which the event occurred, and
398         # - the type of the scanner event.
399         client = event.getClient()
400         aTime = event.getTime()
401         aType = event.getType()
402         
403         # Translate the scanner event type, which is an integer value,
404         # into something readable for logging.
405         typeText = ""
406         if (aType == Scanner.START_SCAN):
407             typeText = "STARTED SCANNER"
408         elif (aType == Scanner.STOP_SCAN):
409             typeText = "STOPPED SCANNER"
410      elif (aType == Scanner.GET_IMAGES):
411             typeText = "IMAGES"
412         else:
413             typeText = "UNKNOWN TYPE"
414         
415         # Create the data point.
416         gdp = GenericDataPoint()
417         gdp.dt = self.dataType
418         gdp.setField("time", str(aTime))
419         gdp.setField("event", str(typeText))
420         
421         # Log the data point.
422         self.logDataPoint(gdp)
423
424
425
426
427
428
429
430 # **** fromDisplay Method ****
431 #
432 # This method is called from NEMO and is passed an
433 # event when a graphic is displayed on the
434 # screen.
435 def fromDisplay(event):
436     global exampleClassInstance
437     
438     exampleClassInstance.handleDisplayEvent(event)
439
440 # **** fromButtonBox Method ****
441 #
442 # This method is called from NEMO and is passed an
443 # event when a button is pressed on a button box.
444 def fromButtonBox(event):
445     global exampleClassInstance
446     
447     exampleClassInstance.handleButtonEvent(event)
448
449 # **** fromScanner Method ****
450 #
451 # This method is called from NEMO and is passed an
452 # event when the scanner performs an operation.
453 def fromScanner(event):
454     global exampleClassInstance
455     
456     exampleClassInstance.handleScannerEvent(event)
457
458 # This method is the entry point. When the experiment starts, this method is called.
459 def start():
460     # The global keyword signifies that the variable is defined in the global scope
461     global exampleClassInstance
462     
463     # Send the resource with the given name to the specified client.
464     task.sendResource(0, 'myImage.jpg')
465     
466     # Call the run method of our ExampleClass instance.
467     exampleClassInstance.run()
468
469
470
471 #
472 # Any code that is not in a method or class is executed at the beginning--
473 # at the time when the script is first interpreted. (Note: you can make calls
474 # to methods and classes, but only those that you call explicitly call will
475 # be executed.
476 #
477 # Hence, the following code is executed automatically before the experiment starts.
478 #
479
480
481 # Specify the number of subjects this experiment requires.
482 task.setExptSize(1)
483
484 # Set the name of this experiment
485 task.setExperimentName("Example Experiment")
486
487 # Specify which function will be called when the expeirment starts.
488 # Note: 'start' is the name of a function. This is not a call to the
489 # 'start' funtion, just a reference to it (note that start is not
490 # followed by it's own open and close parentheses- this means the
491 # function isn't actually being called just referenced).
492 task.setStartFunction(start)
493
494 # Request a Data Type with the name 'example_data_type' from NEMO.
495 dataType = task.getDataType('example_data_type')
496
497 # Specify which devices this experiment will use. NEMO provides a method (addDevice)
498 # to create the devices using the following parameters:
499 # - name : the name of the device, which may be viewed by Operators
500 # - class name : the name of the NEMO class for the desired device
501 # - handler method : a reference to the method that will handle incoming data
502 # from this device
503 # The call to this NEMO method returns a Device Handle. This Device Handle can be
504 # used to communicate to the device.
505 #
506 # Note: When specifying the class name of the device, we're using the logical
507 # device, and the Operator at the client location will pick the corresponding
508 # physical device.
509 displayHandle = task.addDevice("Display", "nemo.pdi.GraphicalDisplay", fromDisplay)
510 scanner = task.addDevice("Scanner", "nemo.pdi.Scanner", fromScanner)
511 buttonBox = task.addDevice("ButtonBox", "nemo.pdi.ButtonBox", fromButtonBox)
512
513 # Create an instance of the Class we defined above.
514 exampleClassInstance = ExampleClass(displayHandle, dataType)