25"""
+26 Parses the given configuration file
+27 line into a tuple.
+28 @param line: The line to parse
+29 @type line: String
+30 @return: Tuple of the form (key, value)
+31 @rtype: Tuple
+32 """
+33split=line.replace(" ","").split("=")
+34iflen(split)!=2:
+35raiseException("Bad config file...")
+36returnsplit[0],split[1]
+
+ Message
+ " A message object to be passed between threads via Queue
+ Sample Usage: Making a new message that will send terminate
+ from main to display: newMessage = Message("Main",
+ "Display", "Terminate", \{\}) Making a
+ slide that will update Display with a new slide: newMessage =
+ Message("Main", "Display", "Update",
+ \{\})newMessage.add_content("slide1",
+ "http://google.com")
+
6""""
+ 7 A message object to be passed between threads via Queue
+ 8 I{Sample Usage}:
+ 9 Making a new message that will send terminate from main to display:
+ 10 C{newMessage = Message("Main", "Display", "Terminate", \{\})}
+ 11 Making a slide that will update Display with a new slide:
+ 12 C{newMessage = Message("Main", "Display", "Update", \{\})}
+ 13 C{newMessage.add_content("slide1", "http://google.com")}
+ 14
+ 15 @attention: It is good practice to use dict accessors as opposed to object attributes
+ 16 @todo: Un-expose attributes
+ 17 @ivar src: Name of the thread who sent the message
+ 18 @type src: String
+ 19 @ivar dest: Intended message destination
+ 20 @type dest: String
+ 21 @ivar pluginDest: Client-side plugin to receive the message
+ 22 @type pluginDest: String
+ 23 @ivar action: Action for the client-side plugin to perform
+ 24 @type action: String
+ 25 @ivar content: Message payload (destination plugin should be able to interpret content)
+ 26 @type content: Dictionary
+ 27 @ivar datetime: Message timestamp
+ 28 @type datetime: String
+ 29 @copyright: Northeastern University Crew 2014
+ 30 """
+ 31
+
58"""
+ 59 Sets the given key-value pair to the message's content
+ 60 @param key: Key to write to in message content
+ 61 @param val: Value to write in message content
+ 62 @return: None
+ 63 """
+ 64self.content[key]=val
+
105"""
+106 Implements dict-like behavior for Messages
+107 @param item: Message value to fetch
+108 @return: The attribute associated with the given key
+109 """
+110ifitem=="src":
+111returnself.src
+112elifitem=="dest":
+113returnself.dest
+114elifitem=="pluginDest":
+115returnself.pluginDest
+116elifitem=="action":
+117returnself.action
+118elifitem=="content":
+119returnself.content
+120elifitem=="datetime":
+121returnself.datetime
+122raiseException("Message does not have an attribute \""+item+"\"")
+
" A message object to be passed between threads via Queue
+ Sample Usage: Making a new message that will send terminate from
+ main to display: newMessage = Message("Main",
+ "Display", "Terminate", \{\}) Making a slide
+ that will update Display with a new slide: newMessage =
+ Message("Main", "Display", "Update",
+ \{\})newMessage.add_content("slide1",
+ "http://google.com")
+
+
+
Attention:
+ It is good practice to use dict accessors as opposed to object
+ attributes
+
2
+ 3
+ 4# TODO: Make symlinks between the Server and Client slide, message, and ConfigParser classes
+ 5-classSlide():
+
6"""
+ 7 Class for Slide Show Slide Objects
+ 8 @ivar url: The Slide's URL
+ 9 @type url: String
+10 @ivar duration: The Duration to show the slide (in seconds)
+11 @type duration: Integer
+12 @ivar id: The Slide's ID
+13 @type id: Integer
+14 @ivar meta: The Slide's meta content
+15 @type meta: Dictionary
+16 @copyright: Northeastern University Crew 2014
+17 """
+18
+19@staticmethod
+
21"""
+22 Slide Constructor based on the given input (instead of a dictionary)
+23 @param url: The Slide's URL
+24 @type url: String
+25 @param duration: The Duration to show the slide (in seconds)
+26 @type duration: Integer
+27 @param id: The Slide's ID
+28 @type id: Integer
+29 @param meta: The Slide's meta content
+30 @type meta: Dictionary
+31 @return: The constructed Slide
+32 @rtype: Slide
+33 """
+34returnSlide({"permalink":url,"duration":duration,"ID":id,"meta":meta})
+
64"""
+65 Predicate method which checks if the given id is equal to the slide's
+66 @param id: The id to check
+67 @type id: Integer
+68 @return: Whether the id matches the slide's
+69 @rtype: Boolean
+70 @todo: Do we I{really} need a method for this?
+71 """
+72returnself.id==id
+
32"""
+33 Sends the given message to the server
+34 @param msg: The message to send
+35 @type msg: String
+36 @return: None
+37 @rtype: NoneType
+38 """
+39self.sock.send(msg)
+
42"""
+43 Connects the Client to the Server
+44 (Server configuration inside of Configs/PIE.conf)
+45 @param runtimeVars: User-defined configuration
+46 @return: None
+47 @rtype: NoneType
+48 """
+49print"I'm connected: "+runtimeVars["name"]
+50whileTrue:
+51try:
+52self.sock.connect((self.hostIP,self.hostPort))
+53print"Connected"
+54break
+55except:
+56print'Unable to connect'
+57time.sleep(2)
+58
+59# Pass along PIE name to Grandma
+60# TODO: Abstract to allow config setting of these parameters
+61identify=Message(runtimeVars["name"],"Grandma","socketServer","connect",{})
+62identify.add_content("name",runtimeVars["name"])
+63self.sock.send(identify.toJSON())
+
This is an example of a plugin that the user could add themselves.
+ There are several rules to create a plugin and have it run, in order to
+ ensure programmatic safety as well as usability. they are as follows:
+
+
+ The file for the plugin must be placed in the Plugins directory
+
+
+ The file must only define one class, nothing more, nothing less
+
+
+ The class must have the EXACT same name as the file its in
+
+
+ the class must extend Plugin
+
+
+ add the class to the config file in the plugins field. The syntax for
+ this field is identical to the python list syntax
+
+
+ The plugin must implement the method getName which returns a string.
+ No other implementations are necessary.
+
+
+
+
+
Note:
+ your plugin will raise an exception if another plugin tries to pass
+ it a message and the addMessage method is not
+ implemented
+
+ 1"""
+ 2This is an example of a plugin that the user could add themselves.
+ 3There are several rules to create a plugin and have it run, in order to ensure
+ 4programmatic safety as well as usability. they are as follows:
+ 5 1. The file for the plugin must be placed in the Plugins directory
+ 6 2. The file must only define one class, nothing more, nothing less
+ 7 3. The class must have the EXACT same name as the file its in
+ 8 4. the class must extend Plugin
+ 9 5. add the class to the config file in the plugins field. The syntax for this field
+10 is identical to the python list syntax
+11 6. The plugin must implement the method getName which returns a string. No other implementations are necessary.
+12@note: your plugin will raise an exception if another plugin tries to pass it a message and the C{addMessage} method is
+13not implemented
+14"""
+15frompluginimportPlugin
+16
+17
+
16"""
+17 The main function of the plugin. This is called
+18 after setup() by the main client function
+19 @param runtimeVars: Configuration provided in Configs/PIE.conf
+20 @return: None
+21 @rtype: NoneType
+22 """
+23pass
+
26"""
+27 Called during the client's initialization process
+28 @param messageDict: All of the Client's Plugins' Message Receivers
+29 @type messageDict: Dictionary
+30 @param runtimeVars: Configuration provided in Configs/PIE.conf
+31 @type runtimeVars: Dictionary
+32 """
+33pass
+
13"""
+ 14 Web Page Display Window Class
+ 15 Represents a full screen GTK Web Browser (no UI)
+ 16 displaying a given URL.
+ 17 @note: URL must begin with a prefix (e.g. "https://")
+ 18 @note: Extension of the python-gtk-webkit presentation program,
+ 19 Copyright (C) 2009 by Akkana Peck
+ 20 @license: GNU Public License, version 2
+ 21 @copyright: Northeastern University Crew 2014
+ 22 """
+ 23
+
91"""
+ 92 GTK Web Display Plugin Class
+ 93 @see: Plugins/plugin.py for more information
+ 94 @copyright: Northeastern University Crew 2014
+ 95 """
+ 96
+
5"""
+ 6 A Thread-Safe Logging Class
+ 7 @todo: Modify to use syslog instead of these log files
+ 8 @copyright: Northeastern University Crew 2014
+ 9 """
+10
+
12"""
+13 Logger Constructor
+14 @param threshold: The amount of messages to store in a buffer
+15 before writing to a logfile
+16 @type threshold: Integer
+17 @return: The Constructed Logger
+18 @rtype: Logger
+19 """
+20self.__debug=[]
+21self.__debugLock=threading.Lock()
+22self.__warning=[]
+23self.__warningLock=threading.Lock()
+24self.__error=[]
+25self.__errorLock=threading.Lock()
+26self.__threshold=threshold
+
+ 1#!/usr/bin/python
+ 2# General Imports
+ 3importthread
+ 4
+ 5fromClasses.ConfigParserimportConfigParser
+ 6
+ 7# Import functions
+ 8
+ 9# In the future we should house these in a file of there own
+10# We can dynamically load plugins from the directory
+11# we wouldn't need to import and add everything by hand.
+12fromPlugins.pluginimportPlugin
+13fromslideShowimportSlideShowPlugin
+14fromsocketClientimportIOPlugin
+15fromgtkDisplayimportGTKPlugin
+16
+17
+
44"""
+45 The main function of the client
+46 @return: None
+47 @rtype: None
+48 """
+49runtimeVars=ConfigParser.readConfig()
+50plugins=[SlideShowPlugin(),IOPlugin()]+getAdditionalPlugins(runtimeVars)
+51runtimeVars["plugins"]+=["SlideShowPlugin","IOPlugin"]
+52
+53defaddPluginToDict(dict,p):
+54dict[p.getName()]=p.addMessage
+55returndict
+
56
+57# messageDict = Message-handling functions for each plugin
+58messageDict=reduce(addPluginToDict,plugins,{})
+59
+60forplugininplugins:
+61plugin.setup(messageDict,runtimeVars)
+62forplugininplugins:
+63print"Starting "+plugin.getName()
+64ifplugin.needsThread():
+65thread.start_new_thread(plugin.run,(runtimeVars,))
+66# Instead of having main() be in a busy wait doing nothing,
+67# we denote GTKPlugin() to be the "main" plugin, so its
+68# behavior drives any looping done on the main thread
+69GTKPlugin().run(runtimeVars)
+70
+71
+72# TODO: Replace with argparse library
+73if__name__=="__main__":
+74main()
+75
+
15"""
+ 16 Slide Show Plugin Class
+ 17 @see: Plugins/plugin.py for more information
+ 18 @copyright: Northeastern University Crew 2014
+ 19 """
+ 20
+
47"""
+ 48 Runs the slide show (manages GTK and incoming messages)
+ 49 @param inputQueue: The Slide Show Plugin's input queue
+ 50 @type inputQueue: Queue.Queue
+ 51 @param runtimeVars: User-defined Configuration
+ 52 @type runtimeVars: Dictionary
+ 53 @param setPage: The function which sets the GTK Display's webpage
+ 54 @type setPage: Function
+ 55 @param writeOut: The socket outgoing message queue
+ 56 @type writeOut: Function
+ 57 @return: None
+ 58 @rtype: None
+ 59 """
+ 60# TODO get DT
+ 61slideRequest=Message(runtimeVars["name"],"Grandma",'WPHandler',"querySlides","Maor slidez")
+ 62print"slideReuqest "+slideRequest.toJSON()
+ 63writeOut(slideRequest)
+ 64
+ 65slides=[]
+ 66# Replace with loading slide
+ 67slides.append(
+ 68Slide.makeSlide("file:///home/pi/dds-client/Client/resources/loading_screen/index.html",5,-1,""))
+ 69
+ 70# Temp Weather slide for testing purposes. Uncomment as needed.
+ 71# slides.append(Slide("http://104.131.73.58", 10))
+ 72
+ 73# Helper to get the slide out of the slide list with the given ID, also insures uniqueness if ID's
+ 74defgetSlideById(slideList,id):
+ 75print"getSlideById called. currentMessage("+str(type(currentMessage))+"):",currentMessage
+ 76matches=filter(lambdax:x.sameID(id),slideList)
+ 77iflen(matches)>1:
+ 78raiseException("Why are there two slides with the same ID?")
+ 79returnNoneiflen(matches)==0elsematches[0]
+
80
+ 81defdeleteSlide(slideList,id):
+ 82print"Deleting slide..."
+ 83temp=getSlideById(slides,id)
+ 84Logger.log("DEBUG","Removing slide: "+str(temp))
+ 85iftemp:
+ 86slideList.remove(temp)
+ 87
+ 88defeditSlide(slideList,id,newSlide):
+ 89print"Editing slide..."
+ 90old=getSlideById(slides,id)
+ 91Logger.log("DEBUG","Changing slide :"+str(old)+" to "+str(newSlide))
+ 92ifold:
+ 93slideList.remove(old)
+ 94slideList.append(newSlide)
+ 95
+ 96defaddSlide(slideList,slide):
+ 97print"Adding slide..."
+ 98Logger.log("DEBUG","Adding slide :"+str(slide))
+ 99slideList.append(slide)
+100
+101x=0
+102Run=True
+103whileRun:
+104printstr(slides)
+105#
+106iflen(slides)>1andgetSlideById(slides,-1)isnotNone:
+107print"Slides have loaded removing loading slide..."
+108# Following line needed for situations in which
+109# a client only has one slide to show
+110x=0
+111slides.remove(getSlideById(slides,-1))
+112iflen(slides)==0:
+113print"I Have no slides, stopping slide show..."
+114Logger.log("ERROR","Ran out of slides...")
+115sys.exit(0)
+116# TODO: Handle this situation better.
+117# (Currently, the slide show will
+118# simply freeze on its last received
+119# slide at this point)
+120currentSlide=slides[x]
+121target_time=datetime.datetime.now()+datetime.timedelta(seconds=currentSlide.duration)
+122setPage(currentSlide.url)
+123print"Just set page : "+currentSlide.url
+124whiledatetime.datetime.now()<target_time:
+125ifnotinputQueue.empty():
+126currentMessage=inputQueue.get()
+127infoForAddition=json.loads(currentMessage["content"])
+128ifcurrentMessage["action"]=="load-slides":
+129forslideJSONininfoForAddition["actions"]:
+130# TODO fields in dds-api call need to be changed to standard,
+131# they also need ID and meta added @Eddie
+132addSlide(slides,
+133Slide.makeSlide(slideJSON["location"],slideJSON["duration"],slideJSON["ID"],
+134""))
+135elifcurrentMessage["action"]=="add-slide":
+136addSlide(slides,Slide(infoForAddition))
+137elifcurrentMessage["action"]=="delete-slide":
+138deleteSlide(slides,infoForAddition["ID"])
+139elifcurrentMessage["action"]=="edit-slide":
+140editSlide(slides,infoForAddition["ID"],Slide(infoForAddition))
+141elifcurrentMessage["action"]=="Terminate":
+142Run=False
+143break
+144time.sleep(1)
+145# Move on
+146iflen(slides):
+147x=(x+1)%len(slides)
+148
+
+
+
+
+
diff --git a/Client/Classes/ConfigParser.py b/Client/Classes/ConfigParser.py
deleted file mode 100644
index a99c5c5..0000000
--- a/Client/Classes/ConfigParser.py
+++ /dev/null
@@ -1,45 +0,0 @@
-class ConfigParser:
- """
- Configuration Parser Class
- @copyright: Northeastern University Crew 2014
- """
-
- @staticmethod
- def readConfig():
- """
- Reads the contents of PIE.conf
- @return: User-specified Configuration
- @rtype: Dict
- """
- config = open("Configs/PIE.conf", "r")
- configContents = config.read()
- configDict = {}
- for line in configContents.splitlines():
- if not (line.startswith("[") or line == ""):
- pair = ConfigParser.getPair(line)
- configDict[pair[0]] = pair[1]
- return configDict
-
- @staticmethod
- def getPair(line):
- """
- Parses the given configuration file
- line into a tuple.
- @param line: The line to parse
- @type line: String
- @return: Tuple of the form (key, value)
- @rtype: Tuple
- """
- split = line.replace(" ", "").split("=")
- if len(split) != 2:
- raise Exception("Bad config file...")
- if split[1].find("[") != -1:
- if split[1] != "[]":
- temp = []
- for string in split[1][1:-1].split(","):
- temp.append(string)
- split[1] = temp
- else:
- split[1] = []
- return split[0], split[1]
-
diff --git a/Client/Classes/QueueDict.py b/Client/Classes/QueueDict.py
deleted file mode 100644
index 051e9b6..0000000
--- a/Client/Classes/QueueDict.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import Queue
-
-
-class QueueDict:
- """
- A wrapper around the Queue Object, which is
- simply a dictionary of Queues that can be
- passed into a plugin, thereby allowing all
- plugins to communicate with other plugins.
- (i.e. A String to Queue dictionary)
- @ivar Queues: Dictionary of queues
- @type Queues: Dictionary
- @ivar queueSize : Queue size in _Queues
- @type queueSize: Integer
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self, queueSize=100):
- """
- Queue Dictionary Constructor
- @param queueSize: (Optional) Specifies size of contained queues (Default 100)
- @return: The Constructed Queue Dictionary
- @rtype: QueueDict
- """
- self.Queues = {}
- self.queueSize = queueSize
-
- def __getitem__(self, name):
- """
- Retrieves the desired queue using dictionary syntax
- (e.g. C{queues[name]})
- @param name: The name of the desired queue
- @type name: String
- @return: The queue corresponding to the given name
- @rtype: Queue.Queue
- """
- return self.Queues[name]
-
- def addQueue(self, name):
- """
- Adds a queue with the given name to the QueueDict object.
- @param name: The name of the queue to add
- @type name: String
- @return: None
- @rtype: NoneType
- """
- self.Queues[name] = Queue.Queue(self.queueSize)
\ No newline at end of file
diff --git a/Client/Classes/ThreadDict.py b/Client/Classes/ThreadDict.py
deleted file mode 100644
index 38f4f7f..0000000
--- a/Client/Classes/ThreadDict.py
+++ /dev/null
@@ -1,33 +0,0 @@
-class ThreadDict:
- """
- Wrapper around thread object, allows us to
- start all the persistent Threads in a consistent
- and simple fashion. See plugins.py for more details.
-
- @ivar Threads: The threads contained in the object.
- @type Threads: Dictionary
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self):
- """
- ThreadDict Constructor
- @return: An empty ThreadDict
- @rtype: ThreadDict
- """
- self.Threads = {}
-
- def addThread(self, name, function, queueDict):
- """
- Adds a thread with the given parameters to the ThreadDict
- @param name: The name of the thread
- @type name: String
- @param function: The function associated with the thread to add
- @type function: Function
- @param queueDict: The QueueDict to add the thread's message queue to
- @type queueDict: QueueDict
- @return: None
- @rtype: NoneType
- """
- queueDict.addQueue(name)
- self.Threads[name] = function
\ No newline at end of file
diff --git a/Client/Classes/__init__.py b/Client/Classes/__init__.py
deleted file mode 100644
index 9c71683..0000000
--- a/Client/Classes/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# This file is needed to allow us to move classes into this folder
-# and call them using Classes.____
diff --git a/Client/Classes/log.py b/Client/Classes/log.py
deleted file mode 100644
index 64b9ecb..0000000
--- a/Client/Classes/log.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# TODO: Figure out if this is even being used...
-class logging(object):
- def __init__(self, arg):
- super(logging, self).__init__()
- self.arg = arg
diff --git a/Client/Classes/message.py b/Client/Classes/message.py
deleted file mode 100644
index fab75a3..0000000
--- a/Client/Classes/message.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import json
-
-
-# TODO: See doctring below
-class Message:
- """"
- A message object to be passed between threads via Queue
- I{Sample Usage}:
- Making a new message that will send terminate from main to display:
- C{newMessage = Message("Main", "Display", "Terminate", \{\})}
- Making a slide that will update Display with a new slide:
- C{newMessage = Message("Main", "Display", "Update", \{\})}
- C{newMessage.add_content("slide1", "http://google.com")}
-
- @attention: It is good practice to use dict accessors as opposed to object attributes
- @todo: Un-expose attributes
- @ivar src: Name of the thread who sent the message
- @type src: String
- @ivar dest: Intended message destination
- @type dest: String
- @ivar pluginDest: Client-side plugin to receive the message
- @type pluginDest: String
- @ivar action: Action for the client-side plugin to perform
- @type action: String
- @ivar content: Message payload (destination plugin should be able to interpret content)
- @type content: Dictionary
- @ivar datetime: Message timestamp
- @type datetime: String
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self, src, dest, pluginDest, action, content, datetime=None):
- """
- Message Constructor
- @param src: Name of the thread who sent the message
- @type src: String
- @param dest: Intended message destination
- @type dest: String
- @param pluginDest: Client-side plugin to receive the message
- @type pluginDest: String
- @param action: Action for the client-side plugin to perform
- @type action: String
- @param content: Message payload (destination plugin should be able to interpret content)
- @type content: Dictionary
- @param datetime: Message timestamp
- @type datetime: String
- @return: Message with the given parameters
- @rtype: Message
- """
- self.src = src
- self.dest = dest
- self.pluginDest = pluginDest
- self.action = action
- self.content = content
- self.datetime = datetime
-
- def add_content(self, key, val):
- """
- Sets the given key-value pair to the message's content
- @param key: Key to write to in message content
- @param val: Value to write in message content
- @return: None
- """
- self.content[key] = val
-
- def toJSON(self):
- """
- Returns the message in JSON format
- @return: JSON representation of Message
- @rtype: String
- """
- text = json.dumps(self.__dict__)
- return text
-
- # TODO: (See docstring)
- def __str__(self):
- """
- @return: String Representation of Message
- @rtype: String
- @todo: This looks horrendous. We should pretty it up.
- """
- return "Src = " + self.src + " dest = " + self.dest + " pluginDest = " + self.pluginDest + \
- " action = " + self.action + " content = " + self.content
-
- @staticmethod
- def fromJSON(jsonObj):
- """
- Creates a message from JSON input
- @param jsonObj: A Parsed JSON Input
- @type jsonObj: Dictionary
- @return: Message represented by the given JSON
- @rtype: Message
- """
- src = jsonObj['src']
- dest = jsonObj['dest']
- pluginDest = jsonObj['pluginDest']
- action = jsonObj['action']
- content = jsonObj['content']
- datetime = None
- if "datetime" in jsonObj:
- datetime = jsonObj["datetime"]
- return Message(src, dest, pluginDest, action, content, datetime)
-
- def __getitem__(self, item):
- """
- Implements dict-like behavior for Messages
- @param item: Message value to fetch
- @return: The attribute associated with the given key
- """
- if item == "src":
- return self.src
- elif item == "dest":
- return self.dest
- elif item == "pluginDest":
- return self.pluginDest
- elif item == "action":
- return self.action
- elif item == "content":
- return self.content
- elif item == "datetime":
- return self.datetime
- raise Exception("Message does not have an attribute \"" + item + "\"")
diff --git a/Client/Classes/slide.py b/Client/Classes/slide.py
deleted file mode 100644
index 71290ff..0000000
--- a/Client/Classes/slide.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import json
-
-
-# TODO: Make symlinks between the Server and Client slide, message, and ConfigParser classes
-class Slide():
- """
- Class for Slide Show Slide Objects
- @ivar url: The Slide's URL
- @type url: String
- @ivar duration: The Duration to show the slide (in seconds)
- @type duration: Integer
- @ivar id: The Slide's ID
- @type id: Integer
- @ivar meta: The Slide's meta content
- @type meta: Dictionary
- @copyright: Northeastern University Crew 2014
- """
-
- @staticmethod
- def makeSlide(url, duration, id, meta):
- """
- Slide Constructor based on the given input (instead of a dictionary)
- @param url: The Slide's URL
- @type url: String
- @param duration: The Duration to show the slide (in seconds)
- @type duration: Integer
- @param id: The Slide's ID
- @type id: Integer
- @param meta: The Slide's meta content
- @type meta: Dictionary
- @return: The constructed Slide
- @rtype: Slide
- """
- return Slide({"permalink": url, "duration": duration, "ID": id, "meta": meta})
-
- def __init__(self, infoDict):
- """
- Slide Constructor
- @param infoDict: The relevant information for the slide
- @type infoDict: Dictionary
- @return: The constructed Slide
- @rtype: Slide
- """
- self.__type__ = "slide"
- print "Got meta:", infoDict["meta"]
- self.url = infoDict["permalink"]
- if (not (isinstance(infoDict["meta"], str)) and
- (infoDict["meta"]["dds_external_url"][0] != "")):
- self.url = infoDict["meta"]["dds_external_url"][0]
-
- self.duration = infoDict["duration"]
- self.id = infoDict["ID"]
- self.meta = infoDict["meta"]
-
- def toJSON(self):
- """
- @return: A JSON Representation of the slide
- @rtype: String
- """
- text = json.dumps(self.__dict__)
- return text
-
- def sameID(self, id):
- """
- Predicate method which checks if the given id is equal to the slide's
- @param id: The id to check
- @type id: Integer
- @return: Whether the id matches the slide's
- @rtype: Boolean
- @todo: Do we I{really} need a method for this?
- """
- return self.id == id
-
- def __str__(self):
- return "Slide[url=" + str(self.url) + ", duration=" + str(self.duration) + ", id=" + str(
- self.id) + ", meta=" + str(self.meta) + "]"
-
- def __repr__(self):
- # Simplified output (Shown when Arrays of Slides are Printed)
- return "Slide(" + str(self.url) + "," + str(self.duration) + ")"
diff --git a/Client/Classes/sockClass.py b/Client/Classes/sockClass.py
deleted file mode 100644
index 617c723..0000000
--- a/Client/Classes/sockClass.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import socket, time
-from message import Message
-
-class sockClass:
- """ Client Socket class. Lets us split off socket functionality into
- multiple threads such that we can send and recive sockets comunications
- without any troubles."""
- def __init__(self, runtimeVars):
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.hostIP = runtimeVars["server"]
- self.hostPort = int(runtimeVars["port"])
- self.connect(runtimeVars)
-
- def send(self, msg):
- self.sock.send(msg)
-
- def connect(self, runtimeVars):
- print "Im conected: "+runtimeVars["name"]
- while True:
- # connect to remote host
- #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- #s.settimeout(2)
- try :
- self.sock.connect((self.hostIP, self.hostPort))
- print "Connected"
- break
- except :
- print 'Unable to connect'
- time.sleep(2)
-
- # Passes along PIE name to Grandma
- # Needs to be abstracted to allow config setting of these parameters
- identify = Message(runtimeVars["name"], "Grandma","socketServer" , "connect",{})
- identify.add_content("name",runtimeVars["name"])
- identify.add_content("item2","bob")
- self.sock.send(identify.toJSON() + '\v')
diff --git a/Client/Classes/socketlist.py b/Client/Classes/socketlist.py
deleted file mode 100644
index ca4e191..0000000
--- a/Client/Classes/socketlist.py
+++ /dev/null
@@ -1,131 +0,0 @@
-class socketList:
- """
- Object responsible for keeping track of all socket connections
-
- @ivar pieMap: A dictionary mapping each connected Raspberry Pi to its socket
- @type pieMap: Dictionary
- @ivar sockList: List of live Socket Connections
- @type sockList: Array
- @ivar serverSocket: Represents the Server's Listening Socket
- @type serverSocket: Socket
- @see: Python C{socket} Documentation for socket info
- @warning: If you're writing a plugin, you I{probably} don't need
- to mess with this.
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self, serverSocket):
- """
- socketList Constructor
- @param serverSocket: The socket to use as the server socket
- @type serverSocket: Socket
- @return: A new empty SocketList with the given server socket
- @rtype: socketList
- """
- self.pieMap = {}
- self.sockList = []
- self.serverSocket = serverSocket
-
- def addSocket(self, sock):
- """
- Adds the given socket to the socketList
- @param sock: The socket to add
- @type sock: Socket
- @return: None
- @rtype: NoneType
- """
- self.sockList.append(sock)
-
- def mapPie(self, sock, pieName):
- """
- Maps the given Raspberry Pi to the given socket
- @param sock: The socket to map the Raspberry Pi to
- @type sock: Socket
- @param pieName: The name of the Raspberry Pi
- @type pieName: String
- @return: None
- @rtype: NoneType
- """
- print "mapping" + pieName
- self.pieMap[pieName] = sock
-
- def removeSocket(self, sock):
- """
- Removes the given socket to the socketList
- @param sock: The socket to remove
- @type sock: Socket
- @return: None
- @rtype: NoneType
- """
- print "\nGood bye, " + str(self.getPie(sock)) + "!\n"
- sock.close()
- self.sockList.remove(sock)
- self.pieMap.pop(self.getPie(sock))
-
- def broadcast(self, msg, sock=None):
- """
- Broadcasts the given message to every socket
- except the serverSocket and the socket provided
- @param msg: The message to broadcast
- @type msg: String
- @param sock: (Optional) The socket to exclude (e.g.
- the socket from which the broadcast
- originated)
- @type sock: Socket
- @return: None
- @rtype: NoneType
- """
- for socket in self.sockList:
- print(self.getPie(socket))
- if socket != self.serverSocket and socket != sock:
- self.sendMessage(socket, msg)
-
- def sendMessage(self, sock, msg):
- """
- Sends the given message to the given socket
- @param sock: The socket to receive the message
- @type sock: Socket
- @param msg: The message to send
- @type msg: String
- @return: None
- @rtype: NoneType
- """
- print "Sending"
- print "Message " + str(msg)
- try:
- sock.sendall(msg)
- print "Message sent!"
- except Exception as e:
- # Is caught if something goes wrong.
- # Print the exception and do
- # any needed cleanups for the socket
- print str(e)
- if sock:
- self.removeSocket(sock)
-
- def getSock(self, pie):
- """
- Returns the socket associated with the given
- Raspberry Pi name
- @param pie: The name of the Raspberry Pi whose socket to retrieve
- @type pie: String
- @return: The socket for the given Raspberry Pi
- @rtype: Socket
- """
- try:
- return self.pieMap[pie]
- except:
- print "Missing Pie:", pie
-
- def getPie(self, sock):
- """
- Returns the Raspberry Pi associated with the given socket
- @param sock: The socket whose Raspberry Pi to retrieve
- @type sock: Socket
- @return: The Raspberry Pi associated with the socket
- @rtype: String
- """
- print(len(self.pieMap))
- for pie, socket in self.pieMap.iteritems():
- if sock == socket:
- return pie
\ No newline at end of file
diff --git a/Client/Configs/PIE.conf b/Client/Configs/PIE.conf
deleted file mode 100644
index 392dd95..0000000
--- a/Client/Configs/PIE.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-[SlideRequests]
-server = 10.0.10.132
-name = keylime
-port = 5000
-[Display]
-width = 1280
-height = 1024
-[Plugins]
-plugins=[]
diff --git a/Client/Plugins/ExamplePlugin.py b/Client/Plugins/ExamplePlugin.py
deleted file mode 100644
index 8255613..0000000
--- a/Client/Plugins/ExamplePlugin.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-This is an example of a plugin that the user could add themselves.
-There are several rules to create a plugin and have it run, in order to ensure
-programmatic safety as well as usability. they are as follows:
- 1. The file for the plugin must be placed in the Plugins directory
- 2. The file must only define one class, nothing more, nothing less
- 3. The class must have the EXACT same name as the file its in
- 4. the class must extend Plugin
- 5. add the class to the config file in the plugins field. The syntax for this field
- is identical to the python list syntax
- 6. The plugin must implement the method getName which returns a string. No other implementations are necessary.
-@note: your plugin will raise an exception if another plugin tries to pass it a message and the C{addMessage} method is
-not implemented
-"""
-from plugin import Plugin
-
-
-class ExamplePlugin(Plugin):
- def getName(self):
- return "ExamplePlugin"
\ No newline at end of file
diff --git a/Client/Plugins/__init__.py b/Client/Plugins/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/Client/Plugins/plugin.py b/Client/Plugins/plugin.py
deleted file mode 100644
index 4a6d65a..0000000
--- a/Client/Plugins/plugin.py
+++ /dev/null
@@ -1,53 +0,0 @@
-class Plugin:
- """
- Plugin Base Class
- """
-
- def needsThread(self):
- """
- Predicate method which asks whether the plugin's
- run() function requires threading
- @return: Whether the plugin needs a thread
- @rtype: Boolean
- """
- return False
-
- def run(self, runtimeVars):
- """
- The main function of the plugin. This is called
- after setup() by the main client function
- @param runtimeVars: Configuration provided in Configs/PIE.conf
- @return: None
- @rtype: NoneType
- """
- pass
-
- def setup(self, messageDict, runtimeVars):
- """
- Called during the client's initialization process
- @param messageDict: All of the Client's Plugins' Message Receivers
- @type messageDict: Dictionary
- @param runtimeVars: Configuration provided in Configs/PIE.conf
- @type runtimeVars: Dictionary
- """
- pass
-
- def getName(self):
- """
- @return: The Plugin's name
- @rtype: String
- """
- raise Exception("Abstract Plugin does not have a name")
-
- def addMessage(self, message):
- """
- Handler for incoming messages
- @param message: Received message
- @type message: String
- """
- raise Exception("Not implemented for Plugin")
-
- def __str__(self):
- return self.getName()
-
-
diff --git a/Client/README.md b/Client/README.md
deleted file mode 100644
index fc0a53b..0000000
--- a/Client/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-Client Functionality:
-===================
-
-Client will be a multi-threaded program that will will take inputs from the server program as well as eventual hardware interactions. It will output to the display (A web browser), to the server, as well as possibly hardware.
-
-The "main" thread essentially is a thread manager that ensures all of the proper children threads are running as well as pass along any information that they can't act upon.
-
-The "display" thread manages "slide show" running on the browser front end as well as waits for any commands to upload the slides in the slide show, remove any slides from the slide show, immediately display on the screen anything it may have to.
-
-The "logging" thread waits input from any threads running and logs it to the syslog.
-
-The "socket" thread handles the transfer of information between the server and the client. It will accomplish this through the use of sockets where PIEs will connect to a socket server running on the server and send a general identifier message. Then messages will pass freely between the server and client.
diff --git a/Client/Resources/loading_screen/index.html b/Client/Resources/loading_screen/index.html
deleted file mode 100644
index 7e6bf00..0000000
--- a/Client/Resources/loading_screen/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
Now Loading
-
\ No newline at end of file
diff --git a/Client/Resources/loading_screen/main.css b/Client/Resources/loading_screen/main.css
deleted file mode 100644
index af3edda..0000000
--- a/Client/Resources/loading_screen/main.css
+++ /dev/null
@@ -1,22 +0,0 @@
-#imgwrap {
- margin-left: auto;
- margin-right: auto;
- width: 100%;
- height: 70%;
-}
-#logo {
- display:block;
- margin-left: auto;
- margin-right: auto;
- max-height: 100%;
- max-width: 100%;}
-#mainText {
- text-align:center;
- font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
- font-weight: 300;
- font-size: 72px;
- color:#594B48;}
-
-body {
- background-color: #E6D4C0;
-}
\ No newline at end of file
diff --git a/Client/Resources/loading_screen/nu_n.png b/Client/Resources/loading_screen/nu_n.png
deleted file mode 100644
index bf2e4de..0000000
Binary files a/Client/Resources/loading_screen/nu_n.png and /dev/null differ
diff --git a/Client/__init__.py b/Client/__init__.py
deleted file mode 100644
index 14082ec..0000000
--- a/Client/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# Exists solely for documentation-generating purposes
\ No newline at end of file
diff --git a/Client/gtkDisplay.py b/Client/gtkDisplay.py
deleted file mode 100755
index 60912ac..0000000
--- a/Client/gtkDisplay.py
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env python
-
-import threading
-import time
-
-import gobject
-import gtk
-import webkit
-from Plugins.plugin import Plugin
-
-
-class WebBrowser(gtk.Window):
- """
- Web Page Display Window Class
- Represents a full screen GTK Web Browser (no UI)
- displaying a given URL.
- @note: URL must begin with a prefix (e.g. "https://")
- @note: Extension of the python-gtk-webkit presentation program,
- Copyright (C) 2009 by Akkana Peck
- @license: GNU Public License, version 2
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self, url, width, height):
- """
- GTK Window Constructor
- @param url: The initial URL to show
- @type url: String
- @param width: The Screen's Width
- @type width: Integer
- @param height: The Screen's Height
- @type height: Integer
- @return: The Constructed WebBrowser
- @rtype: WebBrowser
- """
- gtk.Window.__init__(self)
- self.fullscreen()
-
- self._browser = webkit.WebView()
- settings = webkit.WebSettings()
- settings.set_property('enable-page-cache', True)
- settings.set_property('enable-accelerated-compositing', True)
-
- self._browser.set_settings(settings)
- self.connect('destroy', gtk.main_quit)
- self.add(self._browser)
-
- self._browser.load_uri(url)
- self._browser.set_size_request(int(width), int(height))
- self.show_all()
-
- gobject.threads_init()
-
- def updatePage(self, url):
- """
- Updates the browser to show the given URL
- @param url: The new URL to show
- @type url: String
- @return: None
- @rtype: NoneType
- """
- print "Update page running in : " + threading.currentThread().getName()
- print "updatePage"
- self._browser.load_uri(url)
- try:
- self.fullscreen()
- except:
- pass
- print "showing"
- self.show_all()
-
-
-def getUpdateHandle(runtimeVars):
- """
- Initializes the browser and returns the
- function used to update its page
- @param runtimeVars: User-defined Configuration
- @type runtimeVars: Dictionary
- @return: Function
- """
- browser = WebBrowser("", runtimeVars["width"], runtimeVars["height"])
-
- # GTK Thread-Safe Wrapper for updatePage() method
- def updatePage(url):
- gobject.timeout_add(100, browser.updatePage, url)
-
- return updatePage
-
-
-class GTKPlugin(Plugin):
- """
- GTK Web Display Plugin Class
- @see: Plugins/plugin.py for more information
- @copyright: Northeastern University Crew 2014
- """
-
- def needsThread(self):
- return True
-
- def run(self, runtimeVars):
- gtk.main()
- while True:
- print "Main loop"
- time.sleep(5)
-
- def getName(self):
- return "Gtk Plugin"
-
- def addMessage(self, message):
- raise Exception("GTKPlugin does not take any messages")
\ No newline at end of file
diff --git a/Client/logging.py b/Client/logging.py
deleted file mode 100644
index ea485b9..0000000
--- a/Client/logging.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import threading
-
-
-class Logger:
- """
- A Thread-Safe Logging Class
- @todo: Modify to use syslog instead of these log files
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self, threshold):
- """
- Logger Constructor
- @param threshold: The amount of messages to store in a buffer
- before writing to a logfile
- @type threshold: Integer
- @return: The Constructed Logger
- @rtype: Logger
- """
- self.__debug = []
- self.__debugLock = threading.Lock()
- self.__warning = []
- self.__warningLock = threading.Lock()
- self.__error = []
- self.__errorLock = threading.Lock()
- self.__threshold = threshold
-
- def __addDebugMsg(self, msg):
- self.__debug.append(msg)
- if len(self.__debug) > self.__threshold:
- self.__writeAllAndClear(self.__debug, open("./debug.txt", "a+"), self.__debugLock)
-
- def __addWarningMessage(self, msg):
- self.__warning.append(msg)
- if len(self.__warning) > self.__threshold:
- self.__writeAllAndClear(self.__warning, open("./warning.txt", "a+"), self.__warningLock)
-
- def __addErrorMessage(self, msg):
- self.__error.append(msg)
- if len(self.__error) > self.__threshold:
- self.__writeAllAndClear(self.__error, open("./error.txt", "a+"), self.__errorLock)
-
- def __writeAllAndClear(self, list, file, lock):
- lock.acquire(True)
- for msg in list:
- file.write(msg + "\n")
- file.close()
- lock.release()
- del list[:]
-
- @staticmethod
- def log(level, msg):
- """
- Logs the given message with the given Log Level
- @param level: The given Log Level. ( "DEBUG" | "WARNING" | "ERROR" )
- @type level: String
- @param msg: The message to log
- @type msg: String
- @return:
- """
- if instance is None:
- raise Exception("Logging instance was never created")
- if level == "DEBUG":
- instance.__addDebugMsg(msg)
- elif level == "WARNING":
- instance.__addWarningMessage(msg)
- elif level == "ERROR":
- instance.__addErrorMessage(msg)
-
-# Initialize the Logger
-instance = Logger(15)
\ No newline at end of file
diff --git a/Client/main.py b/Client/main.py
deleted file mode 100755
index 9e3493e..0000000
--- a/Client/main.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/python
-# General Imports
-import thread
-
-from Classes.ConfigParser import ConfigParser
-
-# Import functions
-
-# In the future we should house these in a file of there own
-# We can dynamically load plugins from the directory
-# we wouldn't need to import and add everything by hand.
-from Plugins.plugin import Plugin
-from slideShow import SlideShowPlugin
-from socketClient import IOPlugin
-from gtkDisplay import GTKPlugin
-
-
-def getAdditionalPlugins(runtimeVars):
- """
- Gets any User-Defined Plugins specified in the Configuration
- from ./Plugins
- @param runtimeVars: User-Defined Configuration
- @type runtimeVars: Dictionary
- @return: User-Define Plugins
- @rtype: Array
- """
- plugins = []
- for plugin in runtimeVars["plugins"]:
- try:
- exec "from Plugins." + plugin + " import " + plugin
- instance = eval(plugin + "()")
- if isinstance(instance, Plugin):
- print instance
- plugins.append(instance)
- else:
- print "Huh? what did i get? : " + str(instance)
- except Exception, e:
- print "Couldn't create an instance of a plugin in the config"
- print str(e)
- return plugins
-
-
-def main():
- """
- The main function of the client
- @return: None
- @rtype: None
- """
- runtimeVars = ConfigParser.readConfig()
- plugins = [SlideShowPlugin(), IOPlugin()] + getAdditionalPlugins(runtimeVars)
- runtimeVars["plugins"] += ["SlideShowPlugin", "IOPlugin"]
-
- def addPluginToDict(dict, p):
- dict[p.getName()] = p.addMessage
- return dict
-
- # messageDict = Message-handling functions for each plugin
- messageDict = reduce(addPluginToDict, plugins, {})
-
- for plugin in plugins:
- plugin.setup(messageDict, runtimeVars)
- for plugin in plugins:
- print "Starting " + plugin.getName()
- if plugin.needsThread():
- thread.start_new_thread(plugin.run, (runtimeVars,))
- # Instead of having main() be in a busy wait doing nothing,
- # we denote GTKPlugin() to be the "main" plugin, so its
- # behavior drives any looping done on the main thread
- GTKPlugin().run(runtimeVars)
-
-
-# TODO: Replace with argparse library
-if __name__ == "__main__":
- main()
-
diff --git a/Client/resources/loading_screen/index.html b/Client/resources/loading_screen/index.html
deleted file mode 100644
index 087ffc9..0000000
--- a/Client/resources/loading_screen/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
Now Loading
-
\ No newline at end of file
diff --git a/Client/resources/loading_screen/main.css b/Client/resources/loading_screen/main.css
deleted file mode 100644
index 77c5c1b..0000000
--- a/Client/resources/loading_screen/main.css
+++ /dev/null
@@ -1,26 +0,0 @@
-#imgwrap {
- margin-left: auto;
- margin-right: auto;
- width: 100%;
- height: 70%;
-}
-
-#logo {
- display: block;
- margin-left: auto;
- margin-right: auto;
- max-height: 100%;
- max-width: 100%;
-}
-
-#mainText {
- text-align: center;
- font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
- font-weight: 300;
- font-size: 72px;
- color: #594B48;
-}
-
-body {
- background-color: #E6D4C0;
-}
\ No newline at end of file
diff --git a/Client/resources/loading_screen/nu_n.png b/Client/resources/loading_screen/nu_n.png
deleted file mode 100644
index bf2e4de..0000000
Binary files a/Client/resources/loading_screen/nu_n.png and /dev/null differ
diff --git a/Client/slideShow.py b/Client/slideShow.py
deleted file mode 100644
index 473d604..0000000
--- a/Client/slideShow.py
+++ /dev/null
@@ -1,147 +0,0 @@
-import Queue
-import json
-import datetime
-import time
-import sys
-
-from Classes.message import Message
-from Classes.slide import Slide
-from logging import Logger
-from Plugins.plugin import Plugin
-import gtkDisplay
-
-
-class SlideShowPlugin(Plugin):
- """
- Slide Show Plugin Class
- @see: Plugins/plugin.py for more information
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self):
- self.updateHandle = None
- self.inQ = Queue.Queue(100)
- self.outHandle = None
-
- def needsThread(self):
- return True
-
- def run(self, runtimeVars):
- if self.updateHandle is None or self.outHandle is None:
- raise Exception("Something went wrong...")
- runShow(self.inQ, runtimeVars, self.updateHandle, self.outHandle)
-
- def setup(self, messageDict, runtimeVars):
- self.updateHandle = gtkDisplay.getUpdateHandle(runtimeVars)
- self.outHandle = messageDict["IOPlugin"]
-
- def getName(self):
- return "slideShow"
-
- def addMessage(self, message):
- print "Adding : " + str(message)
- self.inQ.put(message, True)
-
-
-def runShow(inputQueue, runtimeVars, setPage, writeOut):
- """
- Runs the slide show (manages GTK and incoming messages)
- @param inputQueue: The Slide Show Plugin's input queue
- @type inputQueue: Queue.Queue
- @param runtimeVars: User-defined Configuration
- @type runtimeVars: Dictionary
- @param setPage: The function which sets the GTK Display's webpage
- @type setPage: Function
- @param writeOut: The socket outgoing message queue
- @type writeOut: Function
- @return: None
- @rtype: None
- """
- # TODO get DT
- slideRequest = Message(runtimeVars["name"], "Grandma", 'WPHandler', "load-slides", "Maor slidez")
- print "slideReuqest " + slideRequest.toJSON()
- writeOut(slideRequest)
-
- slides = []
- # Replace with loading slide
- slides.append(
- Slide.makeSlide("file:///home/pi/dds-client/Client/resources/loading_screen/index.html", 5, -1, ""))
-
- # Temp Weather slide for testing purposes. Uncomment as needed.
- # slides.append(Slide("http://104.131.73.58", 10))
-
- # Helper to get the slide out of the slide list with the given ID, also insures uniqueness if ID's
- def getSlideById(slideList, id):
- print "getSlideById called. currentMessage(" + str(type(currentMessage)) + "):", currentMessage
- matches = filter(lambda x: x.sameID(id), slideList)
- if len(matches) > 1:
- raise Exception("Why are there two slides with the same ID?")
- return None if len(matches) == 0 else matches[0]
-
- def deleteSlide(slideList, id):
- print "Deleting slide..."
- temp = getSlideById(slides, id)
- Logger.log("DEBUG", "Removing slide: " + str(temp))
- if temp:
- slideList.remove(temp)
-
- def editSlide(slideList, id, newSlide):
- print "Editing slide..."
- old = getSlideById(slides, id)
- Logger.log("DEBUG", "Changing slide :" + str(old) + " to " + str(newSlide))
- if old:
- slideList.remove(old)
- slideList.append(newSlide)
-
- def addSlide(slideList, slide):
- print "Adding slide..."
- Logger.log("DEBUG", "Adding slide :" + str(slide))
- slideList.append(slide)
-
- x = 0
- Run = True
- while Run:
- print str(slides)
- #
- if len(slides) > 1 and getSlideById(slides, -1) is not None:
- print "Slides have loaded removing loading slide..."
- # Following line needed for situations in which
- # a client only has one slide to show
- x = 0
- slides.remove(getSlideById(slides, -1))
- if len(slides) == 0:
- print "I Have no slides, stopping slide show..."
- Logger.log("ERROR", "Ran out of slides...")
- sys.exit(0)
- # TODO: Handle this situation better.
- # (Currently, the slide show will
- # simply freeze on its last received
- # slide at this point)
- currentSlide = slides[x]
- target_time = datetime.datetime.now() + datetime.timedelta(seconds=currentSlide.duration)
- setPage(currentSlide.url)
- print "Just set page : " + currentSlide.url
- while datetime.datetime.now() < target_time:
- if not inputQueue.empty():
- currentMessage = inputQueue.get()
- infoForAddition = json.loads(currentMessage["content"])
- if currentMessage["action"] == "load-slides":
- for slideJSON in infoForAddition["actions"]:
- # TODO fields in dds-api call need to be changed to standard,
- # they also need ID and meta added @Eddie
- addSlide(slides,
- Slide.makeSlide(slideJSON["location"], slideJSON["duration"], slideJSON["ID"],
- ""))
- elif currentMessage["action"] == "add-slide":
- addSlide(slides, Slide(infoForAddition))
- elif currentMessage["action"] == "delete-slide":
- deleteSlide(slides, infoForAddition["ID"])
- elif currentMessage["action"] == "edit-slide":
- editSlide(slides, infoForAddition["ID"], Slide(infoForAddition))
- elif currentMessage["action"] == "Terminate":
- Run = False
- break
- time.sleep(1)
- # Move on
- if len(slides):
- x = (x + 1) % len(slides)
diff --git a/Client/socketClient.py b/Client/socketClient.py
deleted file mode 100755
index 326c6f6..0000000
--- a/Client/socketClient.py
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/python
-import select
-import sys
-import time
-import json
-import Queue
-
-from Classes.message import Message
-from Classes.sockClass import sockClass
-from logging import Logger
-from Plugins.plugin import Plugin
-
-
-def socket_out(currentMessage, socket):
- """
- Writes the given Message to the given socket
- @param currentMessage: The Message to write
- @type currentMessage: Message
- @param socket: The socket to write to
- @type socket: Socket
- @return: None
- @rtype: NoneType
- """
- Logger.log("DEBUG", "Outbound Message: " + currentMessage.toJSON())
- if currentMessage.dest == "Grandma":
- try:
- socket.send(currentMessage.toJSON() + '\v')
- except Exception, e:
- print str(e)
- Logger.log("ERROR", "Socket raised exception on send - exception : " + str(e))
- raise Exception("Can't send message. Socket = " + str(socket))
- else:
- Logger.log("WARNING", "Message not addressed to grandma")
- print "Socket Message Sent!"
-
-
-def socket_in(s, runtimeVars, route):
- """
- Reads and redirects messages received by the client
- @param s: The Client Socket
- @type s: sockClass
- @param runtimeVars: User-defined Configuration
- @type runtimeVars: Dictionary
- @param route: The message handlers for the client plugins
- @type route: Dictionary
- @return: None
- @rtype: NoneType
- """
- run = True
- while run:
- socket_list = [s.sock, ]
- # Get the list of sockets which are readable
- read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
- for sock in read_sockets:
- if sock == s.sock:
- data = sock.recv(4096)
- if not data:
- s.connect(runtimeVars)
- else:
- messages = filter(lambda s: s != "",
- data.split('\v'))
- for currentMessage in messages:
- currentMessage = json.loads(currentMessage)
- if currentMessage["pluginDest"] == "Main":
- sys.exit(0)
- print "Got message : " + str(currentMessage)
- messageDestination = currentMessage["pluginDest"]
- print "Received message sending it too : " + str(messageDestination)
- route[messageDestination](Message.fromJSON(currentMessage))
- else:
- print "Something Goofed"
- # We all program updates will be in multiples of 1 sec,
- # as this is the slide time accuracy
- time.sleep(1)
-
-
-class IOPlugin(Plugin):
- """
- Socket I/O Plugin Class
- @see: Plugins/plugin.py for more information
- @copyright: Northeastern University Crew 2014
- """
-
- def __init__(self):
- self.queue = Queue.Queue(100)
- self.msgRoute = None
- self.socket = None
-
- def needsThread(self):
- return True
-
- def setup(self, messageDict, runtimeVars):
- self.msgRoute = messageDict
- self.socket = sockClass(runtimeVars)
-
- def run(self, runtimeVars):
- if self.msgRoute is None:
- raise Exception("Can't distribute info; message route is None")
- socket_in(self.socket, runtimeVars, self.msgRoute)
-
- def getName(self):
- return "IOPlugin"
-
- def addMessage(self, message):
- print "Attempting to send Message"
- if self.socket is None:
- Logger.log("WARNING", "No socket connection, trying again...")
- time.sleep(.5)
- self.addMessage(message)
- else:
- socket_out(message, self.socket)
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f3fd563..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2014 Northeastern University College of Computer and Information Science's Experimental Systems Group
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index 131a003..0000000
--- a/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-DDS-Client -- Project Bakesale
-================================
-Todo:
-Note: This is a working todo list. Will add things as they come up
-
-Mostly little things. If you have some time, just look for the `todo` tags sprinkled throughout the codebase
-
-DDS is a NU Crew project.
-The MIT License (MIT)
-
-Copyright (c) 2014 Northeastern University College of Computer and Information Science's Experimental Systems Group
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/Sample JSON.txt b/Sample JSON.txt
deleted file mode 100644
index 806a5a8..0000000
--- a/Sample JSON.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-See: https://dds-wp.ccs.neu.edu/wp-admin/admin-ajax.php?action=dds_api&pie_name=chocolate
-{
- "actions":[
- {
- "type":"slide",
- "location":"https:\/\/round-lake.dustinice.workers.dev:443\/http\/m.weather.com\/weather\/tenday\/USMA0046",
- "duration":1
- },
- {
- "type":"slide",
- "location":"https:\/\/round-lake.dustinice.workers.dev:443\/http\/www.northeastern.edu\/news\/",
- "duration":20
- },
- {
- "type":"slide",
- "location":"https:\/\/round-lake.dustinice.workers.dev:443\/http\/dds-wp.ccs.neu.edu\/?slide=cisters-welcome-dinner&pie_name=chocolate",
- "duration":1
- },
- {
- "type":"slide",
- "location":"https:\/\/round-lake.dustinice.workers.dev:443\/http\/dds-wp.ccs.neu.edu\/?slide=fall-masters-student-social&pie_name=chocolate",
- "duration":3
- },
- {
- "type":"slide",
- "location":"https:\/\/round-lake.dustinice.workers.dev:443\/http\/dds-wp.ccs.neu.edu\/?slide=test-ccis-tutoring&pie_name=chocolate",
- "duration":1
- },
- {
- "type":"slide",
- "location":"https:\/\/round-lake.dustinice.workers.dev:443\/http\/dds-wp.ccs.neu.edu\/?slide=cisters&pie_name=chocolate",
- "duration":3
- },
- {
- "type":"slide",
- "location":"https:\/\/round-lake.dustinice.workers.dev:443\/http\/dds-wp.ccs.neu.edu\/?slide=welcome-to-the-ccis-main-office&pie_name=chocolate",
- "duration":5
- }
- ]
-}
-
diff --git a/Server-module.html b/Server-module.html
new file mode 100644
index 0000000..daac426
--- /dev/null
+++ b/Server-module.html
@@ -0,0 +1,162 @@
+
+
+
+
+ Server
+
+
+
+
+
+
+
25"""
+26 Parses the given configuration file
+27 line into a tuple.
+28 @param line: The line to parse
+29 @type line: String
+30 @return: Tuple of the form (key, value)
+31 @rtype: Tuple
+32 """
+33split=line.replace(" ","").split("=")
+34iflen(split)!=2:
+35raiseException("Bad config file...")
+36returnsplit[0],split[1]
+
+ QueueDict
+ A wrapper around the Queue Object, which is simply a dictionary of
+ Queues that can be passed into a plugin, thereby allowing all
+ plugins to communicate with other plugins.
+
5"""
+ 6 A wrapper around the Queue Object, which is
+ 7 simply a dictionary of Queues that can be
+ 8 passed into a plugin, thereby allowing all
+ 9 plugins to communicate with other plugins.
+10 (i.e. A String to Queue dictionary)
+11 @ivar Queues: Dictionary of queues
+12 @type Queues: Dictionary
+13 @ivar queueSize : Queue size in _Queues
+14 @type queueSize: Integer
+15 @copyright: Northeastern University Crew 2014
+16 """
+17
+
29"""
+30 Retrieves the desired queue using dictionary syntax
+31 (e.g. C{queues[name]})
+32 @param name: The name of the desired queue
+33 @type name: String
+34 @return: The queue corresponding to the given name
+35 @rtype: Queue.Queue
+36 """
+37returnself.Queues[name]
+
40"""
+41 Adds a queue with the given name to the QueueDict object.
+42 @param name: The name of the queue to add
+43 @type name: String
+44 @return: None
+45 @rtype: NoneType
+46 """
+47self.Queues[name]=Queue.Queue(self.queueSize)
+
A wrapper around the Queue Object, which is simply a dictionary of
+ Queues that can be passed into a plugin, thereby allowing all plugins to
+ communicate with other plugins. (i.e. A String to Queue dictionary)
2"""
+ 3 Wrapper around thread object, allows us to
+ 4 start all the persistent Threads in a consistent
+ 5 and simple fashion. See plugins.py for more details.
+ 6
+ 7 @ivar Threads: The threads contained in the object.
+ 8 @type Threads: Dictionary
+ 9 @copyright: Northeastern University Crew 2014
+10 """
+11
+
21"""
+22 Adds a thread with the given parameters to the ThreadDict
+23 @param name: The name of the thread
+24 @type name: String
+25 @param function: The function associated with the thread to add
+26 @type function: Function
+27 @param queueDict: The QueueDict to add the thread's message queue to
+28 @type queueDict: QueueDict
+29 @return: None
+30 @rtype: NoneType
+31 """
+32queueDict.addQueue(name)
+33self.Threads[name]=function
+
+ Message
+ " A message object to be passed between threads via Queue
+ Sample Usage: Making a new message that will send terminate
+ from main to display: newMessage = Message("Main",
+ "Display", "Terminate", \{\}) Making a
+ slide that will update Display with a new slide: newMessage =
+ Message("Main", "Display", "Update",
+ \{\})newMessage.add_content("slide1",
+ "http://google.com")
+
6""""
+ 7 A message object to be passed between threads via Queue
+ 8 I{Sample Usage}:
+ 9 Making a new message that will send terminate from main to display:
+ 10 C{newMessage = Message("Main", "Display", "Terminate", \{\})}
+ 11 Making a slide that will update Display with a new slide:
+ 12 C{newMessage = Message("Main", "Display", "Update", \{\})}
+ 13 C{newMessage.add_content("slide1", "http://google.com")}
+ 14
+ 15 @attention: It is good practice to use dict accessors as opposed to object attributes
+ 16 @todo: Un-expose attributes
+ 17 @ivar src: Name of the thread who sent the message
+ 18 @type src: String
+ 19 @ivar dest: Intended message destination
+ 20 @type dest: String
+ 21 @ivar pluginDest: Client-side plugin to receive the message
+ 22 @type pluginDest: String
+ 23 @ivar action: Action for the client-side plugin to perform
+ 24 @type action: String
+ 25 @ivar content: Message payload (destination plugin should be able to interpret content)
+ 26 @type content: Dictionary
+ 27 @ivar datetime: Message timestamp
+ 28 @type datetime: String
+ 29 @copyright: Northeastern University Crew 2014
+ 30 """
+ 31
+
58"""
+ 59 Sets the given key-value pair to the message's content
+ 60 @param key: Key to write to in message content
+ 61 @param val: Value to write in message content
+ 62 @return: None
+ 63 """
+ 64self.content[key]=val
+
105"""
+106 Implements dict-like behavior for Messages
+107 @param item: Message value to fetch
+108 @return: The attribute associated with the given key
+109 """
+110ifitem=="src":
+111returnself.src
+112elifitem=="dest":
+113returnself.dest
+114elifitem=="pluginDest":
+115returnself.pluginDest
+116elifitem=="action":
+117returnself.action
+118elifitem=="content":
+119returnself.content
+120elifitem=="datetime":
+121returnself.datetime
+122raiseException("Message does not have an attribute \""+item+"\"")
+
" A message object to be passed between threads via Queue
+ Sample Usage: Making a new message that will send terminate from
+ main to display: newMessage = Message("Main",
+ "Display", "Terminate", \{\}) Making a slide
+ that will update Display with a new slide: newMessage =
+ Message("Main", "Display", "Update",
+ \{\})newMessage.add_content("slide1",
+ "http://google.com")
+
+
+
Attention:
+ It is good practice to use dict accessors as opposed to object
+ attributes
+
5"""
+ 6 Class for Slide Show Slide Objects
+ 7 @ivar url: The Slide's URL
+ 8 @type url: String
+ 9 @ivar duration: The Duration to show the slide (in seconds)
+10 @type duration: Integer
+11 @ivar id: The Slide's ID
+12 @type id: Integer
+13 @ivar meta: The Slide's meta content
+14 @type meta: Dictionary
+15 @copyright: Northeastern University Crew 2014
+16 """
+17
+18@staticmethod
+
20"""
+21 Slide Constructor based on the given input (instead of a dictionary)
+22 @param url: The Slide's URL
+23 @type url: String
+24 @param duration: The Duration to show the slide (in seconds)
+25 @type duration: Integer
+26 @param id: The Slide's ID
+27 @type id: Integer
+28 @param meta: The Slide's meta content
+29 @type meta: Dictionary
+30 @return: The constructed Slide
+31 @rtype: Slide
+32 """
+33returnSlide({"permalink":url,"duration":duration,"ID":id,"meta":meta})
+
63"""
+64 Predicate method which checks if the given id is equal to the slide's
+65 @param id: The id to check
+66 @type id: Integer
+67 @return: Whether the id matches the slide's
+68 @rtype: Boolean
+69 @todo: Do we I{really} need a method for this?
+70 """
+71returnself.id==id
+
2"""
+ 3 Object responsible for keeping track of all socket connections
+ 4
+ 5 @ivar pieMap: A dictionary mapping each connected Raspberry Pi to its socket
+ 6 @type pieMap: Dictionary
+ 7 @ivar sockList: List of live Socket Connections
+ 8 @type sockList: Array
+ 9 @ivar serverSocket: Represents the Server's Listening Socket
+ 10 @type serverSocket: Socket
+ 11 @see: Python C{socket} Documentation for socket info
+ 12 @warning: If you're writing a plugin, you I{probably} don't need
+ 13 to mess with this.
+ 14 @copyright: Northeastern University Crew 2014
+ 15 """
+ 16
+
18"""
+ 19 socketList Constructor
+ 20 @param serverSocket: The socket to use as the server socket
+ 21 @type serverSocket: Socket
+ 22 @return: A new empty SocketList with the given server socket
+ 23 @rtype: socketList
+ 24 """
+ 25self.pieMap={}
+ 26self.sockList=[]
+ 27self.serverSocket=serverSocket
+
40"""
+ 41 Maps the given Raspberry Pi to the given socket
+ 42 @param sock: The socket to map the Raspberry Pi to
+ 43 @type sock: Socket
+ 44 @param pieName: The name of the Raspberry Pi
+ 45 @type pieName: String
+ 46 @return: None
+ 47 @rtype: NoneType
+ 48 """
+ 49print"mapping"+pieName
+ 50self.pieMap[pieName]=sock
+
84"""
+ 85 Sends the given message to the given socket
+ 86 @param sock: The socket to receive the message
+ 87 @type sock: Socket
+ 88 @param msg: The message to send
+ 89 @type msg: String
+ 90 @return: None
+ 91 @rtype: NoneType
+ 92 """
+ 93print"Sending"
+ 94print"Message "+str(msg)
+ 95try:
+ 96sock.sendall(msg)
+ 97print"Message sent!"
+ 98exceptExceptionase:
+ 99# Is caught if something goes wrong.
+100# Print the exception and do
+101# any needed cleanups for the socket
+102printstr(e)
+103ifsock:
+104self.removeSocket(sock)
+
107"""
+108 Returns the socket associated with the given
+109 Raspberry Pi name
+110 @param pie: The name of the Raspberry Pi whose socket to retrieve
+111 @type pie: String
+112 @return: The socket for the given Raspberry Pi
+113 @rtype: Socket
+114 """
+115try:
+116returnself.pieMap[pie]
+117except:
+118print"Missing Pie:",pie
+
121"""
+122 Returns the Raspberry Pi associated with the given socket
+123 @param sock: The socket whose Raspberry Pi to retrieve
+124 @type sock: Socket
+125 @return: The Raspberry Pi associated with the socket
+126 @rtype: String
+127 """
+128print(len(self.pieMap))
+129forpie,socketinself.pieMap.iteritems():
+130ifsock==socket:
+131returnpie
+
48"""
+49 Sets the given queue as the receiver for any processed POST
+50 Requests sent out from the WordPress Server
+51 @param outboundMessageQueue: The queue to receive WordPress Messages
+52 @type outboundMessageQueue: Queue.Queue
+53 @return: None
+54 @rtype: NoneType
+55 """
+56print"Starting http POST server for wp updates"
+57wpPush.writeOut=outboundMessageQueue
+
61"""
+62 Retrieves a JSON string containing the slides for the
+63 given Raspberry Pi
+64 @param pieName: The name of the Raspberry Pi whose slides to retrieve
+65 @type pieName: String
+66 @param url: The Base URL of the WordPress Site
+67 @type url: String
+68 @return: A JSON Representation of the given Raspberry Pi's slides
+69 @rtype: String
+70 """
+71url="https://"+url+"/wp-admin/admin-ajax.php?action=dds_api&pie_name="+pieName
+72jsonString=str(urllib2.urlopen(url).read().decode("utf-8"))
+73print"Slides for "+pieName+": "+jsonString
+74returnjsonString
+
Sends a terminate message to the threads listening to the given
+ queues
+
+
Parameters:
+
+
queues (Dictionary) - The list of queues to terminate
+
+
Returns: NoneType
+
None
+
+
Note:
+ I added this 'queues' parameter since, as it was written,
+ Queues.Queues was out of scope for this function. I *believe* the
+ way I added its call above replicates the desired behavior. If not,
+ please fix. If so, please remove this message. -<Philip (11 Dec
+ 2014)>
+
21"""
+22 Main server function (manages threads, queues, etc.)
+23 @return: None
+24 @rtype: NoneType
+25 @copyright: Northeastern University Crew 2014
+26 """
+27# Will be generated from config files
+28runtimeVars=ConfigParser.readConfig()
+29
+30# Set up individual queues
+31Queues=QueueDict()
+32Threads=ThreadDict()
+33plugin(Threads,Queues)
+34Queues.Queues["Logging"]=Queue.Queue(100)
+35Queues.Queues["Main"]=Queue.Queue(100)
+36
+37# Order of Queues passed to functions:
+38# Queue passing data into function,
+39# Queue passing data from function to Main,
+40# Queue for logging function
+41thread.start_new_thread(logging_thread,(Queues.Queues["Logging"],runtimeVars))
+42forThreadinThreads.Threads:
+43log(Queues.Queues["Logging"],"Starting thread"+Thread)
+44thread.start_new_thread(Threads.Threads[Thread],(Queues.Queues[Thread],Queues,runtimeVars))
+45
+46Run=True
+47# Forwards messages to appropriate queues and runs appropriate functions
+48whileRun:
+49log(Queues.Queues["Logging"],"Empty Log")
+50whilenotQueues.Queues["Main"].empty():
+51log(Queues.Queues["Logging"],"Entries in Log")
+52currentMessage=Queues.Queues["Main"].get()
+53if"Terminate"incurrentMessage.content:
+54log(Queues.Queues["Logging"],"Terminating")
+55# Terminate all children threads
+56terminate(Queues.Queues)
+57Run=False
+58break
+
62"""
+63 Sends a terminate message to the threads
+64 listening to the given queues
+65 @param queues: The list of queues to terminate
+66 @type queues: Dictionary
+67 @return: None
+68 @rtype: NoneType
+69 @note: I added this 'queues' parameter since, as it was written,
+70 Queues.Queues was out of scope for this function. I *believe*
+71 the way I added its call above replicates the desired
+72 behavior. If not, please fix. If so, please remove this
+73 message. -<Philip (11 Dec 2014)>
+74 """
+75terminateMes=Message("Main","All","All","Terminate",{})
+76terminateMes.add_content("Terminate",True)
+77forqueueinqueues:
+78queue.put(terminateMes)
+
8"""
+ 9 Adds plugin threads to the given ThreadDict and
+10 adds their message Queues to the given QueueDict
+11 @param Threads: The ThreadDict to contain the plugins' threads
+12 @type Threads: ThreadDict
+13 @param Queues: The QueueDict to contain the plugins' message queues
+14 @type Queues: QueueDict
+15 @return: None
+16 @rtype: NoneType
+17 @todo: Abstract this out so that the plugin names aren't hardcoded
+18 (At least on the Server end. We'll see what the Pis can handle)
+19 @copyright: Northeastern University Crew 2014
+20 """
+21Threads.addThread("socketServer",main_socketServer_thread,Queues)
+22Threads.addThread("WPHandler",main_WPHandler_thread,Queues)
+
14"""
+ 15 Handles any new connections
+ 16 @param kwargs: Needed information for the connection
+ 17 @type kwargs: Dictionary
+ 18 @return: The updated parameters of the parameters used to call it
+ 19 @rtype: Dictionary
+ 20 """
+ 21pie=kwargs["currentMessage"]["src"]
+ 22kwargs["connection"].mapPie(kwargs["sock"],pie)
+ 23returnkwargs
+
+ 1fromBaseHTTPServerimportBaseHTTPRequestHandler,HTTPServer
+ 2importjson
+ 3importthread
+ 4
+ 5fromClasses.messageimportMessage
+ 6
+ 7
+ 8# Defaults
+ 9ADDR="0.0.0.0"
+10PORT=12345
+11writeOut=None
+12
+13
+14#Expecting to receive something of the form
+15#{
+16# "datetime" : "2014-11-30T22:04:15+00:00", # ISO 8601, Timestamp of this push
+17# "action" : "add-slide",
+18# "pies" : [
+19# { "name" : "shepard" },
+20# { "name" : "blueberry" }, # allows adding of new fields later on down the road
+21# ],
+22# "content" : { # should definitely be an object for flex and forward compatibility
+23# "ID" : 14, # WordPress Post ID
+24# "Permalink" : "http://dds-wp..." # The URL to the Post; does not append
+25# # ?pie=name or anything fancy, just the permalink
+26# # ... All other WordPress Post Fields... basically json_encode( get_post( $post_id ) )
+27#
+28# # ... and then the post meta for plugins and other cool add-ons
+29# "meta" : {
+30# "key1" : [ "value" ],
+31# "key2" : [
+32# "value1",
+33# "value2",
+34# 3,
+35# {
+36# "meta can be weird" : "remember that"
+37# }
+38# ]
+39# }
+40# }
+41#}
+42
+43
+44
+
52"""
+53 Handles received POST requests
+54 @return: None
+55 @rtype: NoneType
+56 """
+57# writeOut is set externally to be the
+58# socketServer Message Queue
+59globalwriteOut
+60ifwriteOut==None:
+61# Receiving Queue has not been set
+62print"(RequestHandler): writeOut is none. Returning."
+63return
+64if'content-length'notinself.headers:
+65# Malformed Header; content-length needed to know how many bytes to read
+66print'ERROR(RequestHandler): No content-length in post request, exiting'
+67return
+68
+69# Read in the request
+70data=self.rfile.read(int(self.headers.getheader('content-length')))
+71
+72# Log the request
+73print"Http received POST: "+str(data)
+74
+75# Parse the request into JSON
+76info=json.loads(data)
+77
+78forpieininfo["pies"]:
+79writeOut.put(
+80Message("wpPush",pie["name"],"slideShow",info["action"],json.dumps(info["content"]),
+81info["datetime"]))
+
82
+83# Start the HTTP Server to receive any POST requests
+84# (Note: Python Server's Address set within WordPress Configuration)
+85httpd=HTTPServer((ADDR,PORT),RequestHandler)
+86thread.start_new_thread(httpd.serve_forever,())
+87
+
Server.Classes.message.Message:
+ " A message object to be passed between threads via Queue
+ Sample Usage: Making a new message that will send terminate
+ from main to display: newMessage = Message("Main",
+ "Display", "Terminate", \{\}) Making a
+ slide that will update Display with a new slide: newMessage =
+ Message("Main", "Display", "Update",
+ \{\})newMessage.add_content("slide1",
+ "http://google.com")
+
+
rfc822.Message:
+ Represents a single RFC 2822-compliant message.
+
+
mimetools.Message:
+ A derived class of rfc822.Message that knows about MIME headers and
+ contains some hooks for decoding encoded and multipart messages.
+
+
+
+
Client.Classes.message.Message:
+ " A message object to be passed between threads via Queue
+ Sample Usage: Making a new message that will send terminate
+ from main to display: newMessage = Message("Main",
+ "Display", "Terminate", \{\}) Making a
+ slide that will update Display with a new slide: newMessage =
+ Message("Main", "Display", "Update",
+ \{\})newMessage.add_content("slide1",
+ "http://google.com")
+
Server.Classes.QueueDict.QueueDict:
+ A wrapper around the Queue Object, which is simply a dictionary of
+ Queues that can be passed into a plugin, thereby allowing all
+ plugins to communicate with other plugins.
+
Server.Classes.ThreadDict.ThreadDict:
+ Wrapper around thread object, allows us to start all the persistent
+ Threads in a consistent and simple fashion.
+
This document contains the API (Application Programming Interface)
+documentation for this project. Documentation for the Python
+objects defined by the project is divided into separate pages for each
+package, module, and class. The API documentation also includes two
+pages containing information about the project as a whole: a trees
+page, and an index page.
+
+
Object Documentation
+
+
Each Package Documentation page contains:
+
+
A description of the package.
+
A list of the modules and sub-packages contained by the
+ package.
+
A summary of the classes defined by the package.
+
A summary of the functions defined by the package.
+
A summary of the variables defined by the package.
+
A detailed description of each function defined by the
+ package.
+
A detailed description of each variable defined by the
+ package.
+
+
+
Each Module Documentation page contains:
+
+
A description of the module.
+
A summary of the classes defined by the module.
+
A summary of the functions defined by the module.
+
A summary of the variables defined by the module.
+
A detailed description of each function defined by the
+ module.
+
A detailed description of each variable defined by the
+ module.
+
+
+
Each Class Documentation page contains:
+
+
A class inheritance diagram.
+
A list of known subclasses.
+
A description of the class.
+
A summary of the methods defined by the class.
+
A summary of the instance variables defined by the class.
+
A summary of the class (static) variables defined by the
+ class.
+
A detailed description of each method defined by the
+ class.
+
A detailed description of each instance variable defined by the
+ class.
+
A detailed description of each class (static) variable defined
+ by the class.
+
+
+
Project Documentation
+
+
The Trees page contains the module and class hierarchies:
+
+
The module hierarchy lists every package and module, with
+ modules grouped into packages. At the top level, and within each
+ package, modules and sub-packages are listed alphabetically.
+
The class hierarchy lists every class, grouped by base
+ class. If a class has more than one base class, then it will be
+ listed under each base class. At the top level, and under each base
+ class, classes are listed alphabetically.
+
+
+
The Index page contains indices of terms and
+ identifiers:
+
+
The term index lists every term indexed by any object's
+ documentation. For each term, the index provides links to each
+ place where the term is indexed.
+
The identifier index lists the (short) name of every package,
+ module, class, method, function, variable, and parameter. For each
+ identifier, the index provides a short description, and a link to
+ its documentation.
+
+
+
The Table of Contents
+
+
The table of contents occupies the two frames on the left side of
+the window. The upper-left frame displays the project
+contents, and the lower-left frame displays the module
+contents:
+
+
+
+
+ Project Contents...
+
+ API Documentation Frame
+
+
+
+
+ Module Contents ...
+
+
+
+
+
The project contents frame contains a list of all packages
+and modules that are defined by the project. Clicking on an entry
+will display its contents in the module contents frame. Clicking on a
+special entry, labeled "Everything," will display the contents of
+the entire project.
+
+
The module contents frame contains a list of every
+submodule, class, type, exception, function, and variable defined by a
+module or package. Clicking on an entry will display its
+documentation in the API documentation frame. Clicking on the name of
+the module, at the top of the frame, will display the documentation
+for the module itself.
+
+
The "frames" and "no frames" buttons below the top
+navigation bar can be used to control whether the table of contents is
+displayed or not.
+
+
The Navigation Bar
+
+
A navigation bar is located at the top and bottom of every page.
+It indicates what type of page you are currently viewing, and allows
+you to go to related pages. The following table describes the labels
+on the navigation bar. Note that not some labels (such as
+[Parent]) are not displayed on all pages.
+
+
+
+
Label
+
Highlighted when...
+
Links to...
+
+
[Parent]
+
(never highlighted)
+
the parent of the current package
+
[Package]
+
viewing a package
+
the package containing the current object
+
+
[Module]
+
viewing a module
+
the module containing the current object
+
+
[Class]
+
viewing a class
+
the class containing the current object
+
[Trees]
+
viewing the trees page
+
the trees page
+
[Index]
+
viewing the index page
+
the index page
+
[Help]
+
viewing the help page
+
the help page
+
+
+
The "show private" and "hide private" buttons below
+the top navigation bar can be used to control whether documentation
+for private objects is displayed. Private objects are usually defined
+as objects whose (short) names begin with a single underscore, but do
+not end with an underscore. For example, "_x",
+"__pprint", and "epydoc.epytext._tokenize"
+are private objects; but "re.sub",
+"__init__", and "type_" are not. However,
+if a module defines the "__all__" variable, then its
+contents are used to decide which objects are private.
+
+
A timestamp below the bottom navigation bar indicates when each
+page was last updated.
When javascript is enabled, this page will redirect URLs of
+the form redirect.html#dotted.name to the
+documentation for the object with the given fully-qualified
+dotted name.