#ReleaseTask.py # **** Import Section **** # # This section includes all the import files needed for the script. # The first set of imports are jython specific, the second set are # java/nemo specific (but the order is not important) from time import * from random import * from threading import Lock from string import * from thread import * from math import * from nemo.pdi import * from nemo.server.experiment import Task from nemo.messaging import Message from nemo.experiment import GenericDataPoint from java.lang import * from java.util import * # Device interface objects buttonBox = 0 display = 0 # The data type name dtName = "release_task" # The data type dt = 0 dataSet = None # Global variables numClients = 1 wordDataFile = "color_words.txt" waitingForChoice = 0 waitingForMovieChoice = 0 waitingForReady = 0 run = 0 totalRuns = 4 #This method removes all quotes from a given string. This is useful when reading in text from config files. def stripQuotes(data): if (len(data) >= 2): if (data[0] == '"' and data[len(data) - 1] == '"'): return data[1:len(data) - 1] return data # This is a class. It contains variables and methods for a common purpose. # This particualr class, called Word, has methods for parsing data read in # from a configuratoin file. class Word: # The __init__ method is called when the class is instatiated (when # an instance of this class is created. # This is a good place to initialize any variables necessary. # # The first argument for each method in a class should be called 'self'. # 'self' is basically a self-reference, referring to this instance of # a class. def __init__(self, descriptor, info, dataFile): self.descriptors = self.parse(descriptor) self.fields = self.parse(info) self.dataFile = dataFile if (len(self.fields) <> len(self.descriptors)): raise Exception('Descriptor count does not match field count') def parse(self, info): ar = split(info, '\t') for i in range(len(ar)): ar[i] = stripQuotes(ar[i]) return ar #Returns a word from the array that was created when the config file was read in. def getWord(self): return self.fields[0] #Returns the RGB color values from the array that was created when the config file was read in. def getRGB(self): return self.fields[1] + "," + self.fields[2] + "," + self.fields[3] # This particualr class, called WordSet, has methods for reading in data # from a configuratoin file. class WordSet: def __init__(self, dataFile): self.dataFile = dataFile self.loadData(dataFile) def loadData(self, dataFile): file = open(dataFile) line = None count = 0 self.words = [] self.descriptor = file.readline() if (len(self.descriptor) == 0): print 'Data file is empty:', dataFile file.close() return while (1): line = file.readline() line = line.strip() if (len(line) == 0): break count += 1 p = Word(self.descriptor, line, dataFile) self.words.append(p) def getWordCount(self): return len(self.words) def getWord(self, index): return self.words[index] # This particualr class, called Words, has methods for displaying words on the screen. # The call to this method was made at the bottom of this script: # wordDisplay = Words(task, display, dataSet, task.getResourcePath(wordDataFile).getAbsolutePath()) class Words: submitLock = Lock() submitWaitLock = Lock() submitStr = '' lastSubmit = 0 lastShow = time() * 1000 * 5 def __init__(self, task, display, dataSet, wordsDataFile): self.task = task self.display = display self.dataSet = dataSet self.wordsList = WordSet(wordsDataFile) self.createWordGraphic() # Create several graphics for later use and store them in class # variables. def createWordGraphic(self): self.wordGraphic = GraphicFactory.createGraphic(GraphicFactory.DRAW_STRING) self.wordGraphic.name = "text-graphic" self.wordGraphic.description = '' self.wordGraphic.x = Graphic.CENTER self.wordGraphic.y = Graphic.CENTER self.wordGraphic.fontWidth = str(90) self.wordGraphic.fontHeight = str(10) self.wordGraphic.red = 255 self.wordGraphic.blue = 255 self.wordGraphic.green = 255 self.wordGraphic.fontName = "Arial" self.wordGraphic.fontStyle = self.wordGraphic.NORMAL self.wordGraphic.text = '' def start(self): self.doWordRun() def doWordRun(self): self.display.showGraphics(0) sleep(2) # Loop through all the words in the list for i in range(self.wordsList.getWordCount()): # Set the specific word and colors for each word in the list # See def setWord for more detail self.setWord(self.wordsList.getWord(i).getWord(), self.wordsList.getWord(i).getRGB()) # Add the word graphic to the display self.display.addGraphic(0, self.wordGraphic) sleep(1) self.display.clearGraphics(0) sleep(1) return # Set individual parameters for each word in the list # This method parses through the RGB colors and sets each # color respectively. def setWord(self, aWord, rgb): # Set the text self.wordGraphic.text = aWord # Set the description self.wordGraphic.description = aWord # Parse the RGB colors and set them comma1 = rgb.find(',') r = rgb[0:comma1] gb = rgb[comma1+1:len(rgb)] comma2 = gb.find(',') g = gb[0:comma2] b = gb[comma2+1:len(gb)] self.wordGraphic.red = int(r) self.wordGraphic.green = int(g) self.wordGraphic.blue = int(b) # Logs events specific to this class with a timestamp def log(self, time, event): gdp = GenericDataPoint() gdp.dt = dt gdp.setField("time", str(time)) gdp.setField("event", event) if (dataSet <> None): dataSet.logDataPoint(gdp) # This particualr class, called Words, has methods for displaying words on the screen. # The call to this method was made at the bottom of this script: # wordDisplay = Words(task, display, dataSet, task.getResourcePath(wordDataFile).getAbsolutePath()) class Images: submitLock = Lock() submitWaitLock = Lock() submitStr = '' lastSubmit = 0 lastShow = time() * 1000 * 5 def __init__(self, task, display, dataSet, imageList): self.task = task self.display = display self.dataSet = dataSet self.imageList = imageList self.createImageGraphic() # Create several graphics for later use and store them in class # variables. def createImageGraphic(self): self.imageGraphic = GraphicFactory.createGraphic(GraphicFactory.DRAW_IMAGE) self.imageGraphic.name = "image" self.imageGraphic.description = "name=?" self.imageGraphic.x = Graphic.CENTER self.imageGraphic.y = Graphic.CENTER self.imageGraphic.red = 255 self.imageGraphic.blue = 255 self.imageGraphic.green = 255 self.imageGraphic.imageBytes = [] self.imageGraphic.isResource = 1 self.imageGraphic.imageName = "" def start(self): self.doImageRun() def doImageRun(self): self.display.showGraphics(0) sleep(2) # Loop through all the images in the list for i in range(len(self.imageList)): # Search through the string and finds the first "\" subIndex = self.imageList[i].rfind("\\") + 1 length = len(self.imageList[i]) # Grab the image name from the list. You do not need to # include the leading folder names or "\" since the images # are stored on the client if (subIndex == -1): # If there is no "\" in the image name, set the image name self.imageGraphic.imageName = self.imageList[i] else: # If there is a "\" set only the image name after the "\" self.imageGraphic.imageName = self.imageList[i][subIndex:length] # Set the description for the image self.imageGraphic.description = "name=" + str(self.imageList[i]) # Display the image on the display self.display.addGraphic(0, self.imageGraphic) sleep(1) self.display.clearGraphics(0) sleep(1) return class Movies: submitLock = Lock() submitWaitLock = Lock() movieWaitLock = Lock() movieLoadLock = Lock() postMovieSleepTime = 1 lastSubmit = 0 lastShow = time() * 1000 * 5 count = 0 def __init__(self, task, display, dataSet, configFile): self.loadConfig(configFile) self.task = task self.dataSet = dataSet self.display = display self.createGraphics() #Load the config file and parse through the data def loadConfig(self, filename): # Open the file for reading file = open(filename) self.vidList = [] line = None while (1): # Read one line in the file line = file.readline() if (line == ''): # Leave the while loop if there is no # more data in the file break # Remove whitespace line = line.strip() index = line.find('=') comma = line.find(',') if (index <= 0): # If there is no "=" sign in the data # skip the next part continue else: # Parse the data and set each item tuple = [None, None] probe = line[0:index].strip() vid1 = line[index+1:comma].strip() vid2 = line[comma+1:len(line)].strip() if (len(vid1) == 0): continue if (len(vid2) == 0): continue tuple[0] = vid1 tuple[1] = vid2 # Build a list of all the video names from the datafile self.vidList.append([tuple[0], tuple[1]]) # Create several graphics for later use and store them in class # variables. def createGraphics(self): p = GraphicFactory.createGraphic(GraphicFactory.DRAW_STRING) p.name = "video" p.description = "" p.x = Graphic.CENTER p.y = Graphic.CENTER p.fontWidth = "90" p.fontHeight = "15" p.red = 255 p.blue = 255 p.green = 255 p.fontName = "Arial" p.fontStyle = p.BOLD p.text = "" self.cue = p # Loads a movie and creates a lock. The script pauses while locked # and does not continue until the lock has been released. This # specific lock is released by the method movieLoaded() which # is called by fromMovie() def loadMediaFile(self, fileName): global movieThing self.display.hideGraphics(0) message = movieThing.createMessage(0, Movie.LOAD, fileName) movieThing.send(message, 0) # Get the movie-load lock self.movieLoadLock.acquire() # Try to get it again -- blocks until released by event from movie device self.movieLoadLock.acquire() self.movieLoadLock.release() def start(self): # Clear and hide all graphics self.wipeScreen() self.count = 0 sleep(1.5) # Randomly shuffle the outter list # This changes the order of the sports shown shuffle(self.vidList) for x in range(len(self.vidList)): # Randomly shuffle the inner list # This changes the order of whehter a live or # test video is shown first shuffle(self.vidList[x]) for i in range(len(self.vidList)): # If the script is waiting for a submitLock # release it before continuing if (self.submitLock.locked()): self.submitLock.release() # Set the parameters for the cue text. # This text will prompt the user for # input later in the script self.cue.text = "1" self.cue.description = self.cue.text self.display.addGraphic(0, self.cue) sleep(1.5) self.wipeScreen() # Play the video self.playMediaFile(self.vidList[i][0]) sleep(1.5) self.display.showGraphics(0) self.cue.text = "2" self.cue.description = self.cue.text self.display.addGraphic(0, self.cue) sleep(1.5) self.wipeScreen() # Play the video self.playMediaFile(self.vidList[i][1]) sleep(1.5) self.showChoiceScreen() # Get the lock self.submitWaitLock.acquire() # Try to get the lock again -- won't work until it has been released from the prior acquire self.submitWaitLock.acquire() self.submitWaitLock.release() self.wipeScreen() self.count = self.count + 1 sleep(1.5) self.blankOut() def playMediaFile(self, fileName): # Loads the current movie self.loadMediaFile(fileName) # Sends a message to the server to start the movie that # was just loaded message = movieThing.createMessage(0, Movie.START, "") movieThing.send(message, 0) # Get the movie-over lock self.movieWaitLock.acquire() # Try to get it again -- blocks until released by eventglobal graphicalDisplay from movie device self.movieWaitLock.acquire() self.movieWaitLock.release() # Hide and close the movie message = movieThing.createMessage(0, Movie.HIDE, "") movieThing.send(message, 0) message = movieThing.createMessage(0, Movie.CLOSE, "") movieThing.send(message, 0) # Displays the text "1 or 2" on the screen # This is a prompt for the user to select # which movie the user liked more, either # the first or the second movie. def showChoiceScreen(self): global display global waitingForMovieChoice display.clearGraphics(0) g = GraphicFactory.createGraphic(GraphicFactory.DRAW_STRING) g.name = "choice-screen" g.description = "choice-screen" g.x = Graphic.CENTER g.y = Graphic.CENTER g.fontWidth = "90" g.fontHeight = "15" g.red = 255 g.blue = 255 g.green = 255 g.fontName = "Arial" g.fontStyle = g.BOLD g.text = "1 or 2" display.addGraphic(0, g) display.showGraphics(0) waitingForMovieChoice = 1 # Allows input from the user and logs the entry def submit(self, aTime, key): if (not self.submitWaitLock.locked()): print "submit wait lock not locked" return if (self.submitLock.acquire(0) == 0): print "submit lock already acquired" return print 'submit lock acquired' self.lastSubmit = aTime self.submitWaitLock.release() waitingForMovieChoice = 0 self.log(aTime, "keypress: submit " + str(key)) def movieOver(self): # When the movie is over, release # the lock that was waiting for the # movie to complete self.movieWaitLock.release() def movieLoaded(self): # When the movie loads completely # release the lock that was waiting # for it to load self.movieLoadLock.release() def blankOut(self): # Clear all graphics on the display self.display.clearGraphics(0) def wipeScreen(self): # Clear and hide all graphics on the display self.display.clearGraphics(0) self.display.showGraphics(0) # Log events specific to this Class def log(self, time, event): gdp = GenericDataPoint() gdp.dt = dt gdp.setField("time", str(time)) gdp.setField("event", event) if (dataSet <> None): dataSet.logDataPoint(gdp) # This method is called from NEMO and is passed an # event when the scanner performs an operation. # The events are then logged in the output def fromScanner(event): global dataSet global dt client = event.getClient() aTime = event.getTime() aType = event.getType() type = "" if (aType == Scanner.START_SCAN): type = "STARTED SCANNER" elif (aType == Scanner.STOP_SCAN): type = "STOPPED SCANNER" elif (aType == Scanner.GET_IMAGES): type = "IMAGES" else: type = "UNKNOWN TYPE" gdp = GenericDataPoint() gdp.dt = dt gdp.setField("time", String.valueOf(aTime)) gdp.setField("event", str(type)) if (dataSet <> None): dataSet.logDataPoint(gdp) # This method is called from NEMO and is passed an # event when a button is pressed on a button box. # This method allows control over where the # script continues after is has received # input from the user. # The events are then logged in the output def fromButtonBox(event): global display global waitingForMovieChoice global waitingForReady global waitingForChoice global listType global run key = event.getData() if (len(key) == 0): return if (not key[0] in (letters+digits)): return client = event.getClient() pressTime = event.getTime() if (waitingForReady): if (key == "r" or key == "R"): waitingForReady = 0 else: print "Press R to continue" return if (waitingForMovieChoice): if (key in ["1", "2"]): # Call the submit method in the Movies Class movieDisplay.submit(pressTime, key) return if (waitingForChoice): if (key in ["1","2","3","q","Q"]): waitingForChoice = 0 # Call the startRun() method startRun(key) return # This method is called from NEMO and is passed an # event when a graphic is displayed on the # screen. # The events are then logged in the output def fromDisplay(event): global dataSet global dt global run global motor s = "" gs = event.getGraphics() for i in gs: s = s + i + " " client = event.getClient() aTime = event.getTime() typeText = "" if (event.getType() == GraphicalDisplay.HIDE): typeText = "HIDE" elif (event.getType() == GraphicalDisplay.SHOW): typeText = "SHOW" elif (event.getType() == GraphicalDisplay.DRAW): typeText = "ADD GRAPHIC" elif (event.getType() == GraphicalDisplay.CLEAR_GRAPHICS): typeText = "CLEAR ALL" elif (event.getType() == GraphicalDisplay.ADD_GRAPHICS): typeText = "ADD GRAPHICS" elif (event.getType() == GraphicalDisplay.CREATE_GROUP): typeText = "ADD GRAPHIC GROUP" gdp = GenericDataPoint() gdp.dt = dt gdp.setField("time", String.valueOf(aTime)) gdp.setField("event", str(typeText) + ":{" + s + "}") if (dataSet <> None): dataSet.logDataPoint(gdp) # This method is called from NEMO and is passed an # event when a movie is displayed on the # screen. # The events are then logged in the output def fromMovie(event): movieDisplay.log(event.getTime(), event.getData()) if (event.getType() == Movie.STARTED): print event.getTime(), "movie started" elif (event.getType() == Movie.STOPPED): print event.getTime(), "movie stopped" movieDisplay.movieOver() elif (event.getType() == Movie.LOADED): print event.getTime(), "movie loaded" movieDisplay.movieLoaded() elif (event.getType() == Movie.END_OF_MEDIA): print event.getTime(), "movie over" movieDisplay.movieOver() else: print event.getTime(), "movie event [", event.getData(), "]" # Prompts the user to select a task to run # After each task is run, the script returns to this # menu. def showChoiceScreen(): global display global waitingForChoice display.clearGraphics(0) display.hideGraphics(0) # Calculate center points for each word centerX1 = 0.0/3.0 * 100 + 1.0/60.0 * 100 centerX2 = 1.0/3.0 * 100 + 1.0/60.0 * 100 centerX3 = 2.0/3.0 * 100 + 1.0/60.0 * 100 # Calculate width of each word baseWidth = 1.0/3.0 * 100 - 2.0/60.0 * 100 display.addGraphic(0, makeTextXY("Words", "(1) Words", baseWidth, 5, centerX1, 25, 255, 255, 255)) display.addGraphic(0, makeTextXY("Images", "(2) Images", baseWidth, 5, centerX2, 25, 255, 255, 255)) display.addGraphic(0, makeTextXY("Movies", "(3) Movies", baseWidth, 5, centerX3, 25, 255, 255, 255)) display.addGraphic(0, makeTextXY("msg", "Please choose a task", 40, 15, Graphic.CENTER, 75, 255, 255, 255)) display.addGraphic(0, makeTextXY("msg2", "Hit 'q' to quit", 40, 5, Graphic.CENTER, 85, 255, 255, 255)) display.showGraphics(0) waitingForChoice = 1 def startRun(key): global display global task global run global wordDisplay global imageDisplay global dataSet global dt global key display.clearGraphics(0) # Starts the task and creates the dataSet task.startRun(str(run)) dataSet = task.startDataSet(dt.getName(), 0) if (key == "1"): # Autostart the scanner scanner.startScanning(0) retVal = None # Start the Words Task retVal = wordDisplay.start() elif (key == "2"): # Autostart the scanner scanner.startScanning(0) retVal = None # Start the Images Task retVal = imageDisplay.start() elif (key == "3"): # Autostart the scanner scanner.startScanning(0) retVal = None # Start the Movie Task retVal = movieDisplay.start() # End the current task and dataSet task.endDataSet(dataSet) task.endRun() dataSet = None display.clearGraphics(0) run = run + 1 sleep(2) if (key not in ["q","Q"]): showChoiceScreen() else: retVal = None end(retVal) # Displays "Experiment Complete" to the # display and ends the experiment def end(text): global task global display msg = "Experiment Complete" if (text <> None and len(text) > 0): msg = msg + " : " + text display.clearGraphics(0) sleep(0.5) display.addGraphic(0, makeText(msg, 12.5)) display.showGraphics(0) sleep(2.0) task.endExperiment() # This is a simple method that takes text and its height # and displays it on the screen def makeText(text, height): p = GraphicFactory.createGraphic(GraphicFactory.DRAW_STRING) p.name = "text-graphic" p.description = text p.x = Graphic.CENTER p.y = Graphic.CENTER p.fontWidth = "90" p.fontHeight = str(height) p.red = 255 p.blue = 255 p.green = 255 p.fontName = "Arial" p.fontStyle = p.BOLD p.text = text return p # This method is very similar to makeText, but allows # for more control over the text. It allows you to # define the name, description, starting position of # the text (x,y) and the text color (r,g,b) def makeTextXY(name, desc, w, h, x, y, r, g, b): p = GraphicFactory.createGraphic(GraphicFactory.DRAW_STRING) p.name = str(name) p.description = str(desc) p.x = str(x) p.y = str(y) p.fontWidth = str(w) p.fontHeight = str(h) p.red = r p.green = g p.blue = b p.fontName = "Arial" p.fontStyle = p.BOLD p.text = desc return p #This method is similar to makeTextXY, but instead # of drawing text, it draws a circle with diameter (d) def makeFilledCircleXY(name, desc, x, y, d, r, g, b): p = GraphicFactory.createGraphic(GraphicFactory.FILL_CIRCLE) p.name = str(name) p.description = desc p.x = str(x) p.y = str(y) p.diameter = str(d) p.red = r p.green = g p.blue = b return p # This method is the entry point (see task.setStartFunction(...) below). # Initialize the state here in the script, and send a message to clients # telling them to display the initial screen.""" def start(): global task global dt global dataSet global numClients display.showGraphics(0) display.addGraphic(0, makeText("Preloading Images...", "4")) for j in range(len(imageList)): # Send the resource with the given name to the specified client. task.sendResource(0, imageList[j]) sleep(2) display.clearGraphics(0) showChoiceScreen() # # The following code is always executed. # # Create the Task object. task.setExptSize(numClients) task.setStartFunction(start) # Set up data types and such dt = task.getDataType(dtName) # Set the name of this experiment task.setExperimentName("Release Task") # Specify which devices this experiment will use. NEMO provides a method (addDevice) # to create the devices using the following parameters: # - name : the name of the device, which may be viewed by Operators # - class name : the name of the NEMO class for the desired device # - handler method : a reference to the method that will handle incoming data # from this device # The call to this NEMO method returns a Device Handle. This Device Handle can be # used to communicate to the device. # # Note: When specifying the class name of the device, we're using the logical # device, and the Operator at the client location will pick the corresponding # physical device. display = task.addDevice("Display", "nemo.pdi.GraphicalDisplay", fromDisplay) scanner = task.addDevice("Scanner", "nemo.pdi.Scanner", fromScanner) buttonBox = task.addDevice("ButtonBox", "nemo.pdi.ButtonBox", fromButtonBox) movieThing = task.addDevice("Movie", "nemo.pdi.Movie", fromMovie) # Grabs all files located in the folder of the given name ie "\testimages" imageList = task.getResourceList("testimages") # Create an instance of the Class we defined above. wordDisplay = Words(task, display, dataSet, task.getResourcePath(wordDataFile).getAbsolutePath()) # Create an instance of the Class we defined above. imageDisplay = Images(task, display, dataSet, imageList) # Create an instance of the Class we defined above. movieDisplay = Movies(task, display, dataSet, task.getResourcePath('testmovies.config').getAbsolutePath())