aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Russell Cohen <rcoh@mit.edu>2010-11-23 00:24:08 -0500
committerGravatar Russell Cohen <rcoh@mit.edu>2010-11-23 00:24:08 -0500
commit99325eca1bbe7adc532a63fb0ec06e593b4f9b39 (patch)
tree251a2d1c6ea4814747bf7bc1c3174129198e9bfb
parenta9b3325a9171abd0785bd03cbcb51cbbf47da1f2 (diff)
First Commit with the code. In process of doing some refactoring.
-rw-r--r--Behavior.py33
-rw-r--r--DebugBehavior.py8
-rw-r--r--DecayEvent.py12
-rw-r--r--EchoBehavior.py12
-rw-r--r--IndoorRenderer.py31
-rw-r--r--Input.py46
-rw-r--r--LayoutEngine.py30
-rw-r--r--Light.py51
-rw-r--r--LightInstallation.py91
-rw-r--r--LightStrip.py42
-rw-r--r--LightSystem.py30
-rw-r--r--LineLayout.py5
-rw-r--r--PixelEvent.py13
-rw-r--r--PixelStrip.py42
-rw-r--r--PygameInput.py17
-rw-r--r--PygameRenderer.py20
-rw-r--r--Renderer.py12
-rw-r--r--Screen.py31
-rw-r--r--SmootCoreObject.py23
-rw-r--r--StepEvent.py14
-rw-r--r--TCPInput.py51
-rw-r--r--UDPInput.py16
-rw-r--r--Util.py118
-rw-r--r--ZigzagLayout.py45
-rw-r--r--gfxdemo.py82
25 files changed, 875 insertions, 0 deletions
diff --git a/Behavior.py b/Behavior.py
new file mode 100644
index 0000000..e25f7be
--- /dev/null
+++ b/Behavior.py
@@ -0,0 +1,33 @@
+#Abstract class for a behavior. On every time step, the behavior is passed the
+#inputs from all sensors it is bound to as well as any recursive inputs that it
+#spawned during the last time step. Inheriting classes MUST define
+#processResponse. processResponse should return a list of dictionaries which
+#define the properties of the light response. They must give a location and
+#color. They may define a function pointer which defines a custom mapping.
+#[More on this later. Bug Russell if you want to do it].
+#recursiveResponse to queue a input on the next iteration with a dictionary
+#argument. This will be passed in via recursive inputs.
+import pdb
+from SmootCoreObject import SmootCoreObject
+#timeStep is called on every iteration of the LightInstallation
+#addInput is called on each individual input received, and the inputs queue
+class Behavior(SmootCoreObject):
+ def init(self):
+ self.validateArgs('Behavior.params')
+ if type(self['Inputs']) != type([]):
+ self['Inputs'] = [self['Inputs']]
+ self.recursiveResponseQueue = []
+ self.sensorResponseQueue = []
+ self.outGoingQueue = []
+ def processResponse(self, sensorInputs, recursiveInputs):
+ pass
+ def addInput(self, sensorInputs):
+ self.sensorResponseQueue.append(sensorInputs)
+ def recursiveReponse(self, args):
+ self.responseQueue.append(args)
+ def timeStep(self):
+ responses = self.processResponse(self.sensorResponseQueue, \
+ self.recursiveResponseQueue)
+ self.sensorResponseQueue = []
+ self.recursiveResponseQueue = []
+ return responses
diff --git a/DebugBehavior.py b/DebugBehavior.py
new file mode 100644
index 0000000..f0758be
--- /dev/null
+++ b/DebugBehavior.py
@@ -0,0 +1,8 @@
+from Behavior import Behavior
+import Util
+import pdb
+class DebugBehavior(Behavior):
+ def processResponse(self, sensorInputs, recursiveInputs):
+ if sensorInputs != []:
+ print 'Sensor Inputs: ', sensorInputs
+ return []
diff --git a/DecayEvent.py b/DecayEvent.py
new file mode 100644
index 0000000..def4284
--- /dev/null
+++ b/DecayEvent.py
@@ -0,0 +1,12 @@
+from PixelEvent import PixelEvent
+import Util, math
+class DecayEvent(PixelEvent):
+ def initEvent(self):
+ self.validateArgs('DecayEvent.params')
+ self['Coefficient'] = abs(self['Coefficient'])
+ def lightState(self,timeDelay):
+ if self['DecayType'] == 'Exponential':
+ decay = math.exp(timeDelay*-1*self['Coefficient'])
+ if self['DecayType'] == 'Proportional':
+ decay = float(self['Coefficient']) / timeDelay
+ return Util.multiplyColor(self['Color'], decay)
diff --git a/EchoBehavior.py b/EchoBehavior.py
new file mode 100644
index 0000000..6d0a79b
--- /dev/null
+++ b/EchoBehavior.py
@@ -0,0 +1,12 @@
+from Behavior import Behavior
+import Util
+import pdb
+class EchoBehavior(Behavior):
+ def processResponse(self, sensorInputs, recursiveInputs):
+ ret = []
+ for sensory in sensorInputs:
+ outDict = {}
+ outDict[Util.location] = sensory[Util.location]
+ outDict['Color'] = (255,0,0)
+ ret.append(outDict)
+ return ret
diff --git a/IndoorRenderer.py b/IndoorRenderer.py
new file mode 100644
index 0000000..b42c337
--- /dev/null
+++ b/IndoorRenderer.py
@@ -0,0 +1,31 @@
+from Renderer import Renderer
+import socket, Util
+import pdb
+kinetPort = 6038
+class IndoorRenderer(Renderer):
+ def initRenderer(self):
+ #pdb.set_trace()
+ self.stripLocations = {} #Dict that stores info necessary to render to
+ #strips
+ self.sockets = {} #dict of (IP,port)->Socket
+ #a strip
+# g self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ powerSupplies = self.argDict['PowerSupply']
+ if not type(powerSupplies) == type([]):
+ powerSupplies = [powerSupplies]
+ for powerSupply in powerSupplies:
+ ip = powerSupply['IP']
+ stripsInPowerSupply = powerSupply['PortMapping']
+ for stripId in stripsInPowerSupply:
+ self.stripLocations[stripId] = (ip, \
+ stripsInPowerSupply[stripId])
+ def render(self, lightSystem):
+ for pixelStrip in lightSystem.pixelStrips:
+ stripId = pixelStrip.argDict['Id']
+ (ip, port) = self.stripLocations[stripId]
+ if not ip in self.sockets: #do we have a socket to this
+ #strip? if not, spin off a new one
+ self.sockets[ip] = Util.getConnectedSocket(ip,kinetPort)
+ packet = Util.composePixelStripPacket(pixelStrip, port)
+ self.sockets[ip].send(packet, 0x00)
+
diff --git a/Input.py b/Input.py
new file mode 100644
index 0000000..1ba4528
--- /dev/null
+++ b/Input.py
@@ -0,0 +1,46 @@
+import threading,time,Util
+#Abstract class for inputs. Inheriting classes should call "respond" to raise
+#their event. Inheriting classes MUST define sensingLoop. Called at the
+#interval specified in RefreshInterval while the input is active. For example, if you are writing
+#webserver, this is where the loop should go.
+#Inheriting classes MAY define inputInit. This is called before the loop
+#begins.
+import pdb
+class Input(threading.Thread):
+ #Event scope is a function pointer the function that will get called when
+ #an Parent is raised.
+ def __init__(self, argDict):
+ self.eventQueue = []
+ self.parentScope = argDict['parentScope']
+ self.argDict = argDict
+ if not 'InputId' in argDict:
+ raise Exception('InputId must be defined in config xml')
+ if not 'RefreshInterval' in argDict:
+ print 'RefreshInterval not defined. Defaulting to .5s.'
+ self.argDict['RefreshInterval'] = 500
+ self.inputInit()
+ threading.Thread.__init__(self)
+ self.daemon = True #This kills this thread when the main thread stops
+ def respond(self, eventDict):
+ #if eventDict != []:
+ #pdb.set_trace()
+ self.parentScope.processResponse(self.argDict, eventDict)
+ def newEvent(self, event): #Mostly just useful for grabbing events from the
+ #computer running the sim (key presses, clicks etc.)
+ self.eventQueue.append(event)
+ def parentAlive(self):
+ try:
+ parentAlive = self.parentScope.alive()
+ return parentAlive
+ except:
+ return False
+ def run(self):
+ while self.parentAlive():
+ time.sleep(self.argDict['RefreshInterval']/float(1000))
+ self.sensingLoop()
+ def sensingLoop(self):
+ pass
+ def inputInit(self):
+ pass
+
+
diff --git a/LayoutEngine.py b/LayoutEngine.py
new file mode 100644
index 0000000..6d7cdd5
--- /dev/null
+++ b/LayoutEngine.py
@@ -0,0 +1,30 @@
+from SmootCoreObject import SmootCoreObject
+import Util
+import pdb
+class LayoutEngine(SmootCoreObject):
+ def init(self):
+ self.validateArgs('LayoutEngine.params')
+ self.initLayout()
+ def layoutFunc(self, lastLocation): #Must be defined by inheriting class.
+ #Returns tuple pair (x,y)
+ pass
+ def getLightLocations(self): #returns a complete list of locations of lights
+ #for a strip
+ locations = [self.argDict['originLocation']]
+ for lightIndex in range(self['numLights']-1): #-1 because origin
+ #already exists
+ newLocation = self.layoutFunc(locations[-1])
+ if newLocation == None:
+ raise Exception('Location cannot be null. layoutFunc not \
+ defined or improperly defined.')
+ if Util.dist(newLocation, locations[-1]) > \
+ self['lightToLightSpacing']:
+ raise Exception('Illegal light location. Distance \
+ between adjacent lights must be less than \
+ lightToLightSpacing.')
+ locations.append(newLocation)
+ return locations
+ def initLayout(self):
+ pass
+ def getStripArgs(self): #TODO: triage and remove
+ return self.argDict
diff --git a/Light.py b/Light.py
new file mode 100644
index 0000000..f6a73e8
--- /dev/null
+++ b/Light.py
@@ -0,0 +1,51 @@
+import Util
+import pdb
+from StepEvent import StepEvent
+#Light keeps a cue of events (LightEvent objects). Every time is state is
+#requested, it processes all the members of its cue. If a member returns none,
+#it is removed from the queue. Otherwise, its value added to the lights color
+#weighted by z-index.
+class Light:
+ radius = 2
+ lightOn = False
+ timeOff = -1
+ def __init__(self, location, color):
+ self.location = location
+ self.color = color
+ self.events = {}
+ def turnOn(self):
+ self.lightOn = True
+ def turnOnFor(self, time):
+ event = StepEvent.generate(time, (255,0,255)) #TODO: Move color to
+ self.processInput(event, 0)
+ #arg
+ def processInput(self,lightEvent,zindex): #consider migrating arg to dict
+ self.events[Util.time()] = (zindex, lightEvent)
+ def turnOff(self):
+ self.lightOn = False
+ def lightState(self):
+ deadEvents = []
+ currentTime = Util.time()
+ resultingColor = (0,0,0)
+ for eventTime in self.events: #TODO: right color weighting code
+ (zindex,event) = self.events[eventTime]
+ eventResult = event.lightState(currentTime-eventTime)
+ if eventResult != None:
+ resultingColor = Util.combineColors(eventResult, resultingColor)
+ print resultingColor
+ else:
+ deadEvents.append(eventTime)
+ [self.events.pop(event) for event in deadEvents]
+ if sum(resultingColor) > 0:
+ print resultingColor
+ return tuple(resultingColor)
+ def isLightOn(self):
+ if self.timeOff == -1:
+ return self.lightOn
+ else:
+ return Util.time() < self.timeOff
+ def flip(self):
+ self.lightOn = not self.lightOn
+ def __str__(self):
+ return 'Loc: ' + str(self.location)
+
diff --git a/LightInstallation.py b/LightInstallation.py
new file mode 100644
index 0000000..0c24981
--- /dev/null
+++ b/LightInstallation.py
@@ -0,0 +1,91 @@
+from xml.etree.ElementTree import ElementTree
+from Screen import Screen
+from PixelStrip import PixelStrip
+import pdb, sys, time, Util
+from pygame.locals import *
+#Python class to instantiate and drive a Screen through different patterns,
+#and effects.
+class LightInstallation:
+ def __init__(self, configFileName):
+ self.inputs = {} #dict of inputs and their bound behaviors, keyed by InputId
+ self.behaviors = {}
+ config = ElementTree()
+ config.parse(configFileName)
+ self.screen = Screen()
+ rendererConfig = config.find('RendererConfiguration')
+ layoutConfig = config.find('LayoutConfiguration')
+ inputConfig = config.find('InputConfiguration')
+ behaviorConfig = config.find('BehaviorConfiguration')
+ self.initializeLights(layoutConfig)
+ self.initializeRenderers(rendererConfig)
+ self.initializeInputs(inputConfig)
+ self.initializeBehaviors(behaviorConfig)
+
+ self.mainLoop()
+ def initializeLights(self, layoutConfig):
+ layoutEngines = self.initializeComponent(layoutConfig)
+ [self.addPixelStrip(l) for l in layoutEngines]
+ def addPixelStrip(self, layoutEngine):
+ pixelStrip = PixelStrip(layoutEngine)
+ self.screen.addStrip(pixelStrip)
+ def initializeInputs(self, inputConfig):
+ inputs = self.initializeComponent(inputConfig)
+ for inputClass in inputs:
+ inputClass.start()
+ self.inputs[inputClass.argDict['InputId']] = (inputClass, [])
+ def initializeRenderers(self, rendererConfig):
+ self.renderers = self.initializeComponent(rendererConfig)
+ print self.renderers
+ def initializeComponent(self, config):
+ components = []
+ if config != None:
+ for configItem in config.getchildren():
+ className = configItem.find('Class').text
+ exec('from ' + className + ' import ' + className)
+ args = Util.generateArgDict(configItem.find('Args'))
+ args['parentScope'] = self
+ components.append(eval(className+'(args)')) #TODO: doesn't error
+ #right
+ return components
+ def alive(self):
+ return True
+ def mainLoop(self):
+ #self.screen.allOn()
+ while 1:
+ time.sleep(.1)
+ responses = []
+ for behaviorId in self.behaviors:
+ [responses.append(b) for b in \
+ self.behaviors[behaviorId].timeStep()] #processes all queued inputs
+ [self.screen.respond(response) for response in responses if
+ response != []]
+ self.screen.timeStep()
+ if responses != []:
+ print responses
+ [r.render(self.screen) for r in self.renderers]
+ def initializeBehaviors(self, behaviorConfig):
+ behaviors = self.initializeComponent(behaviorConfig)
+ for behavior in behaviors:
+ print behavior.argDict
+ self.addBehavior(behavior)
+ print self.inputs
+ print self.behaviors
+ def addBehavior(self, behavior):
+ self.behaviors[behavior.argDict['behaviorId']] = behavior
+ for inputId in behavior.argDict['Inputs']:
+ self.inputs[inputId][1].append(behavior.argDict['behaviorId'])
+ def processResponse(self,inputDict, responseDict):
+ #pdb.set_trace()
+ inputId = inputDict['InputId']
+ boundBehaviors = self.inputs[inputId][1]
+ [self.behaviors[b].addInput(responseDict) for b in boundBehaviors]
+
+def main(argv):
+ print argv
+ if len(argv) == 1:
+ l = LightInstallation('LightInstallationConfig.xml')
+ else:
+ l = LightInstallation(argv[1])
+if __name__ == "__main__":
+ main(sys.argv)
+
diff --git a/LightStrip.py b/LightStrip.py
new file mode 100644
index 0000000..7314e42
--- /dev/null
+++ b/LightStrip.py
@@ -0,0 +1,42 @@
+from Light import Light
+from StepEvent import StepEvent
+import pygame
+import math
+import Util
+import pdb
+#Python class representing a single light strip (usually 50 lights)
+class PixelStrip:
+ def __init__(self, layoutEngine):
+ self.initStrip(layoutEngine)
+ self.argDict = layoutEngine.getStripArgs()
+ def initStrip(self, layoutEngine):
+ lightLocations = layoutEngine.getLightLocations()
+ self.lights = [Light(l, (0,0,0)) for l in lightLocations]
+ def __iter__(self):
+ return self.lights.__iter__()
+ def render(self, surface):
+ [l.render(surface) for l in self.lights]
+ #step
+ def allOn(self, time):
+ [l.turnOnFor(time) for l in self.lights]
+ def turnOnLight(self,light, dist):
+ if(dist < 12):
+ light.turnOnFor(300)
+ def respond(self, responseInfo):
+ print 'PixelEvent', responseInfo
+ location = responseInfo[Util.location]
+ if not 'PixelEvent' in responseInfo:
+ if 'Color' in responseInfo:
+ color = responseInfo['Color']
+ else:
+ raise Exception('Need Color. Probably')
+ responseInfo['PixelEvent'] = StepEvent.generate(300, color)
+ (dist, light) = self.getLightNearest(location)
+ light.processInput(responseInfo['PixelEvent'], 0) #TODO: z-index
+
+ def getLightNearest(self, location):
+ dists = [(Util.dist(location, light.location), light) for light in self.lights]
+ dists.sort()
+ return dists[0]
+ #just for now.
+
diff --git a/LightSystem.py b/LightSystem.py
new file mode 100644
index 0000000..e00c4c0
--- /dev/null
+++ b/LightSystem.py
@@ -0,0 +1,30 @@
+from Light import Light
+from LightStrip import LightStrip
+import itertools
+class Screen:
+ def __init__(self):
+ self.responseQueue = []
+ self.lightStrips = []
+ def addStrip(self, lS):
+ self.lightStrips.append(lS)
+ def render(self, surface):
+ [lS.render(surface) for lS in self.lightStrips]
+ def allOn(self):
+ [lS.allOn(-1) for lS in self.lightStrips]
+ #increment time -- This processes all queued responses. Responses generated
+ #during this period are added to the queue that will be processed on the next
+ #time step.
+ def __iter__(self): #the iterator of all our light strips chained togther
+ return itertools.chain(*[strip.__iter__() for strip in self.lightStrips])
+ def timeStep(self):
+ tempQueue = list(self.responseQueue)
+ self.responseQueue = []
+ for response in tempQueue:
+ self.processResponse(response)
+ def respond(self, responseInfo):
+ print responseInfo
+ self.responseQueue.append(responseInfo)
+ def processResponse(self, responseInfo): #we need to make a new dict for
+ #each to prevent interference
+ [strip.respond(dict(responseInfo)) for strip in self.lightStrips]
+
diff --git a/LineLayout.py b/LineLayout.py
new file mode 100644
index 0000000..f5cb5ce
--- /dev/null
+++ b/LineLayout.py
@@ -0,0 +1,5 @@
+from LayoutEngine import LayoutEngine
+#Simple layout class that simply makes a line of LEDs
+class LineLayout(LayoutEngine):
+ def layoutFunc(self, lastLocation):
+ return (lastLocation[0]+self.argDict['spacing'], lastLocation[1])
diff --git a/PixelEvent.py b/PixelEvent.py
new file mode 100644
index 0000000..2c3fa66
--- /dev/null
+++ b/PixelEvent.py
@@ -0,0 +1,13 @@
+#Class defining a light response. Inheriting classes should define lightState,
+#which should return a color, or None if the response is complete. Consider
+#requiring a generate event.
+from SmootCoreObject import SmootCoreObject
+class PixelEvent(SmootCoreObject):
+ def init(self):
+ self.validateArgs('PixelEvent.params')
+ self.initEvent()
+ def initEvent(self):
+ pass
+ def lightState(self,timeDelay):
+ pass
+
diff --git a/PixelStrip.py b/PixelStrip.py
new file mode 100644
index 0000000..7314e42
--- /dev/null
+++ b/PixelStrip.py
@@ -0,0 +1,42 @@
+from Light import Light
+from StepEvent import StepEvent
+import pygame
+import math
+import Util
+import pdb
+#Python class representing a single light strip (usually 50 lights)
+class PixelStrip:
+ def __init__(self, layoutEngine):
+ self.initStrip(layoutEngine)
+ self.argDict = layoutEngine.getStripArgs()
+ def initStrip(self, layoutEngine):
+ lightLocations = layoutEngine.getLightLocations()
+ self.lights = [Light(l, (0,0,0)) for l in lightLocations]
+ def __iter__(self):
+ return self.lights.__iter__()
+ def render(self, surface):
+ [l.render(surface) for l in self.lights]
+ #step
+ def allOn(self, time):
+ [l.turnOnFor(time) for l in self.lights]
+ def turnOnLight(self,light, dist):
+ if(dist < 12):
+ light.turnOnFor(300)
+ def respond(self, responseInfo):
+ print 'PixelEvent', responseInfo
+ location = responseInfo[Util.location]
+ if not 'PixelEvent' in responseInfo:
+ if 'Color' in responseInfo:
+ color = responseInfo['Color']
+ else:
+ raise Exception('Need Color. Probably')
+ responseInfo['PixelEvent'] = StepEvent.generate(300, color)
+ (dist, light) = self.getLightNearest(location)
+ light.processInput(responseInfo['PixelEvent'], 0) #TODO: z-index
+
+ def getLightNearest(self, location):
+ dists = [(Util.dist(location, light.location), light) for light in self.lights]
+ dists.sort()
+ return dists[0]
+ #just for now.
+
diff --git a/PygameInput.py b/PygameInput.py
new file mode 100644
index 0000000..12ae7f6
--- /dev/null
+++ b/PygameInput.py
@@ -0,0 +1,17 @@
+import time, Util
+from Input import Input
+import pygame
+from pygame.locals import *
+#This class processes input from an already running pygame instance and passes
+#it to the parent. This class requires an already running pygame instance.
+class PygameInput(Input):
+ def sensingLoop(self):
+ #try:
+ for event in pygame.event.get():
+ if event.type is KEYDOWN:
+ self.respond({Util.location: (5,5),'Key': event.key})
+ if event.type is MOUSEBUTTONDOWN:
+ self.respond({Util.location: pygame.mouse.get_pos()})
+ #except:
+ #raise Exception('Pygame not initialized. Pygame must be \
+ #initialized.')
diff --git a/PygameRenderer.py b/PygameRenderer.py
new file mode 100644
index 0000000..fb157a4
--- /dev/null
+++ b/PygameRenderer.py
@@ -0,0 +1,20 @@
+from Renderer import Renderer
+import pygame
+from pygame.locals import *
+import pdb
+class PygameRenderer(Renderer):
+ def initRenderer(self):
+ pygame.init()
+ self.screen = pygame.display.set_mode((1300,50))
+ self.background = pygame.Surface(self.screen.get_size())
+ self.background = self.background.convert()
+ self.background.fill(Color('Black'))
+ def render(self, lightSystem):
+ self.background.fill(Color('Black'))
+ #print 'drawing color:',light.color
+ for light in lightSystem:
+ pygame.draw.circle(self.background, light.lightState(), light.location, \
+ light.radius)
+
+ self.screen.blit(self.background, (0,0))
+ pygame.display.flip()
diff --git a/Renderer.py b/Renderer.py
new file mode 100644
index 0000000..2297f1f
--- /dev/null
+++ b/Renderer.py
@@ -0,0 +1,12 @@
+#Renderer abstract class. Doesn't do much now, but might do more later.
+#Inheriting classes MUST define render which takes a light system and renders it.
+#Inheriting classes may define initRenderer which is called after the dictionary
+#is pulled from config.
+from SmootCoreObject import SmootCoreObject
+class Renderer(SmootCoreObject):
+ def init(self):
+ self.initRenderer()
+ def render(lightSystem):
+ pass
+ def initRenderer(self):
+ pass
diff --git a/Screen.py b/Screen.py
new file mode 100644
index 0000000..b05ce02
--- /dev/null
+++ b/Screen.py
@@ -0,0 +1,31 @@
+from Light import Light
+from PixelStrip import PixelStrip
+import itertools
+class Screen:
+ def __init__(self):
+ self.responseQueue = []
+ self.pixelStrips = []
+ def addStrip(self, lS):
+ self.pixelStrips.append(lS)
+ def render(self, surface):
+ [lS.render(surface) for lS in self.pixelStrips]
+ def allOn(self):
+ [lS.allOn(-1) for lS in self.pixelStrips]
+ def __iter__(self): #the iterator of all our light strips chained togther
+ return itertools.chain(*[strip.__iter__() for strip in self.pixelStrips])
+ #increment time -- This processes all queued responses. Responses generated
+ #during this period are added to the queue that will be processed on the next
+ #time step.
+ def timeStep(self):
+ tempQueue = list(self.responseQueue)
+ self.responseQueue = []
+ for response in tempQueue:
+ self.processResponse(response)
+ #public
+ def respond(self, responseInfo):
+ self.responseQueue.append(responseInfo)
+ #private
+ def processResponse(self, responseInfo): #we need to make a new dict for
+ #each to prevent interference
+ [strip.respond(dict(responseInfo)) for strip in self.pixelStrips]
+
diff --git a/SmootCoreObject.py b/SmootCoreObject.py
new file mode 100644
index 0000000..74e1a9a
--- /dev/null
+++ b/SmootCoreObject.py
@@ -0,0 +1,23 @@
+import Util
+import pdb
+class SmootCoreObject:
+ def __init__(self, argDict):
+ self.argDict = argDict
+ self.init() #call init of inheriting class
+ def init(self):
+ pass
+ def __setitem__(self,k, item):
+ self.argDict[k] = item
+ def __getitem__(self, item):
+ if item in self.argDict:
+ return self.argDict[item]
+ else:
+ return None
+ def __getiter__(self):
+ return self.argDict.__getiter__()
+ def validateArgs(self, argFileName):
+ self.validateArgDict(Util.fileToDict(argFileName))
+ def validateArgDict(self, validationDict):
+ for item in validationDict:
+ if not item in self.argDict:
+ raise Exception(validationDict[item])
diff --git a/StepEvent.py b/StepEvent.py
new file mode 100644
index 0000000..e28f0b7
--- /dev/null
+++ b/StepEvent.py
@@ -0,0 +1,14 @@
+from PixelEvent import PixelEvent
+class StepEvent(PixelEvent):
+ def initEvent(self):
+ self.validateArgs('StepEvent.params')
+ def lightState(self,timeDelay):
+ if timeDelay < self['LightTime'] or self['LightTime'] == -1:
+ return self['Color']
+ else:
+ return None
+ @staticmethod
+ def generate(onTime, color):
+ args = {'LightTime': onTime, 'Color': color}
+ return StepEvent(args)
+
diff --git a/TCPInput.py b/TCPInput.py
new file mode 100644
index 0000000..903b6f0
--- /dev/null
+++ b/TCPInput.py
@@ -0,0 +1,51 @@
+import SocketServer
+import Util, Input
+
+"""
+A rough sketch about how a TCP socket server receives data from the phone (or other stuff).
+Some corrections are probably needed from Russell.
+Looks good to me -- not really the way I envisioned it, but since the server
+we're using has a built in loop. When we call the reponse method to pass the
+data up the pipe, we should use the sensingLoop so that everything stays
+thread-safe.
+"""
+class TCPInput(Input.Input):
+ class InputTCPHandler(SocketServer.BaseRequestHandler):
+ def handle(self):
+ # get data from the TCP socket connected to the client
+ self.data = self.request.recv(1024).strip()
+ print "%s wrote:" % self.client_address[0]
+ print self.data
+
+ pydict = json.loads(self.data) # decode and add to queue
+ self.responseQueue.append(pydict)
+
+ """
+ do something to the dict
+ """
+
+ self.request.send("yes") # send back confirmation.
+
+ def inputInit(self):
+ # initialize
+ self.host = "localhost"
+ self.port = 9999
+ self.responseQueue = []
+ # start server
+ self.server = SocketServer.TCPServer((self.host, self.port), InputTCPHandler)
+ self.server.responseQueue = self.responseQueue
+ self.server.serve_forever() # server keeps running till Ctrl+C or self.server.shutdown() is called.
+
+ def sensingLoop(self):
+ # loop action handled through TCPHandler?
+ # if check says to shut down the server, shut it.
+ if self.doShutDown():
+ self.server.shutdown()
+ else:
+ for event in self.responseQueue:
+ self.respond(event)
+ self.responseQueue = []
+
+ def doShutDown(self):
+ # do some checks to see if server should be shut down
+ return False;
diff --git a/UDPInput.py b/UDPInput.py
new file mode 100644
index 0000000..f0af2b0
--- /dev/null
+++ b/UDPInput.py
@@ -0,0 +1,16 @@
+import Util, Input
+import socket
+class UDPInput(Input.Input):
+ def inputInit(self):
+ HOST = '' # Symbolic name meaning all available interfaces
+ PORT = self.argDict['Port'] # Arbitrary non-privileged port
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.sock.bind((HOST, PORT))
+ print 'UDPINIT'
+ def sensingLoop(self):
+ print 'udploop'
+ (data,address) = self.sock.recvfrom(1024)
+ dataDict = {'data':data, 'address':address}
+ print 'LOLOLOLOL'
+ self.respond(dataDict)
+
diff --git a/Util.py b/Util.py
new file mode 100644
index 0000000..9bb3888
--- /dev/null
+++ b/Util.py
@@ -0,0 +1,118 @@
+import pdb
+from xml.etree.ElementTree import ElementTree
+import math,struct
+#import json # json.loads() to decode string; json.dumps() to encode data
+import socket
+from pygame.locals import *
+import time as clock
+KINET_VERSION = 0x0001
+KINET_MAGIC = 0x4adc0104
+KINET_MOREMAGIC = 0xdeadbeef
+KINET_DEEPMAGIC = 0xc001d00d
+KINET_MAGICHASH = 0x69000420
+KINET_PORTOUT = 0x0108
+KINET_UNI = 0
+kinetDict = {'flags': 0, 'startcode': 0, 'pad':0}
+def dist(l1, l2):
+ return math.sqrt(sum([(l1[i]-l2[i])**2 for i in range(len(l1))]))
+def time():
+ return clock.time()*1000
+def fileToDict(fileName):
+ fileText = ''
+ with open(fileName) as f:
+ for line in f:
+ fileText += line.rstrip('\n').lstrip('\t') + ' '
+ if fileText == '':
+ return {}
+ return eval(fileText)
+print fileToDict('LayoutEngine.params')
+def combineColors(c1,c2):
+ return [c1[i]+c2[i] for i in range(min(len(c1),len(c2)))]
+def multiplyColor(color, percent):
+ return tuple([channel*(percent) for channel in color])
+#parses arguments into python objects if possible, otherwise leaves as strings
+def generateArgDict(parentNode, recurse=False):
+ args = {}
+ for arg in parentNode.getchildren():
+ key = arg.tag
+ if arg.getchildren() != []:
+ value = generateArgDict(arg, True)
+ else:
+ #convert into python if possible, otherwise don't
+ try:
+ value = eval(arg.text)
+ except (NameError,SyntaxError):
+ value = str(arg.text)
+ if key in args: #build of lists of like-elements
+ if type(args[key]) != type([]):
+ args[key] = [args[key]]
+ args[key].append(value)
+ else:
+ args[key]=value
+ #if we should be a list but we aren't:
+ if len(args.keys()) == 1 and recurse:
+ return args[args.keys()[0]]
+ return args
+def getConnectedSocket(ip,port):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ print (ip, port)
+ sock.connect((ip, port))
+ return sock
+def composePixelStripData(pixelStrip):
+ packet = bytearray()
+ for light in pixelStrip:
+ color = light.lightState()
+ for channel in color: #skip the last value, its an
+ #alpha value
+ packet.append(struct.pack('B', channel))
+ #pdb.set_trace()
+ return packet
+def composePixelStripPacket(pixelStrip,port):
+ packet = bytearray()
+ data = composePixelStripData(pixelStrip)
+ subDict = dict(kinetDict)
+ subDict['len'] = 38399 #I have no idea why this works.
+ subDict['port'] = port
+ #pdb.set_trace()
+ packet.extend(kinetPortOutPacket(subDict))
+ packet.append(0x0)
+ packet.extend(data)
+ return packet
+def kinetHeader():
+ header = bytearray()
+ header.extend(struct.pack('L', KINET_MAGIC))
+ header.extend(struct.pack('H', KINET_VERSION))
+ header.extend(struct.pack('H', KINET_PORTOUT))
+ header.extend(struct.pack('L', 0))
+ return header
+def kinetPortOut():
+ header = kinetHeader()
+ header.extend(struct.pack('L', KINET_UNI))
+ return header
+def kinetPortOutPayload(argDict):
+ payload = bytearray()
+ payload.extend(struct.pack('B', argDict['port']))
+ #payload.append(0x00) #somepadding? lolwtf.
+ payload.extend(struct.pack('H', argDict['flags']))
+ #payload.append(0x00) #somepadding? lolwtf.
+ payload.extend(struct.pack('H', argDict['len']))
+ payload.extend(struct.pack('H', argDict['startcode']))
+ #pdb.set_trace()
+ return payload
+def kinetPortOutPacket(payloadArgs):
+ packet = bytearray()
+ packet.extend(kinetPortOut())
+ packet.extend(kinetPortOutPayload(payloadArgs))
+ return packet
+def testXMLParse(fileName):
+ #pdb.set_trace()
+ config = ElementTree()
+ config.parse(fileName)
+ print generateArgDict(config.find('ChildElement'))
+ print generateArgDict(config.find('Renderer'))
+testXMLParse('TestXML.xml')
+
+##CONSTANTS##
+location = 'Location'
+
+
diff --git a/ZigzagLayout.py b/ZigzagLayout.py
new file mode 100644
index 0000000..66d27ec
--- /dev/null
+++ b/ZigzagLayout.py
@@ -0,0 +1,45 @@
+from LayoutEngine import LayoutEngine
+import pdb
+#Slightly more complex layout class that makes a zig-Zag Led Pattern
+#Inheriting classes must specify zigLength, the length in lights of a of a zig
+#and zig Axis, the direction of the long X axis (X or Y).
+#EG: zig length = 4, zig Axis = X would give:
+# X-X-X-X
+# |
+# X-X-X-X
+# |
+# X-X-X-X etc.
+class ZigzagLayout(LayoutEngine):
+ def initLayout(self):
+ if not 'zigLength' in self.argDict:
+ raise Exception('zigLength must be defined in argDict')
+ if not 'zigAxis' in self.argDict:
+ raise Exception('zigAxis must be defined in argDict')
+ if not 'xDirection' in self.argDict:
+ self.argDict['xDirection'] = 1 #right
+ if not 'yDirection' in self.argDict:
+ self.argDict['yDirection'] = 1 #down
+ def layoutFunc(self, lastLocation):
+ if not 'buildQueue' in self.argDict:
+ self.argDict['buildQueue'] = self.argDict['zigLength']
+
+ newLoc = list(lastLocation)
+ if self.argDict['buildQueue'] > 1:
+ if self.argDict['zigAxis'] == 'X':
+ newLoc[0] += self.argDict['spacing'] * self.argDict['xDirection']
+ else:
+ newLoc[1] += self.argDict['spacing'] * self.argDict['yDirection']
+ self.argDict['buildQueue'] -= 1
+ else:
+ self.argDict['buildQueue'] = self.argDict['zigLength']
+ if self.argDict['zigAxis'] == 'X':
+ newLoc[1] += self.argDict['spacing'] * self.argDict['yDirection']
+ else:
+ newLoc[0] += self.argDict['spacing'] * self.argDict['xDirection']
+ if self.argDict['zigAxis'] == 'X':
+ self.argDict['xDirection'] *= -1
+ else:
+ self.argDict['yDirection'] *= -1
+ return newLoc
+
+
diff --git a/gfxdemo.py b/gfxdemo.py
new file mode 100644
index 0000000..7e242ae
--- /dev/null
+++ b/gfxdemo.py
@@ -0,0 +1,82 @@
+import os, sys, random, Util
+import pygame
+import math
+from Light import Light
+from LightStrip import LightStrip
+from LightSystem import LightSystem
+from pygame.locals import *
+spacing = 4
+vspacing = 12
+def dist(l1, l2):
+ return math.sqrt(sum([(l1[i]-l2[i])**2 for i in range(len(l1))]))
+def colorAdd(c1, c2):
+ if(c1 == None):
+ return c2
+ if(c2 == None):
+ return c1
+ c = [min(c1[i]+c2[i], 255) for i in range(4)]
+ return Color(*c)
+pygame.color.add = colorAdd
+class BouncyLightSystem(LightSystem):
+ def respond(self, responseInfo):
+ location = responseInfo['location']
+ data = responseInfo['data']
+ if(location[0] < 0):
+ data = 'right'
+ if(location[0] > self.length):
+ data = 'left'
+ if(data == None):
+ data = 'right'
+ if data == 'right':
+ change = 20
+ else:
+ change = -20
+ responseInfo['location'] = (location[0]+change, location[1])
+ responseInfo['data'] = data
+ LightSystem.respond(self,responseInfo)
+class BouncyLightSystemMultiple(BouncyLightSystem):
+ def respondToInput(self, inputDict):
+ if sum(inputDict['mouse']) % 2 == 0:
+ color = Color('Blue')
+ else:
+ color = Color('Red')
+ LightSystem.respond(self, {'location':inputDict['mouse'],'data': 'right',
+ 'color': color})
+class DyingLightSystem(LightSystem):
+ def respond(self, inputDict):
+ if 'responsesLeft' in inputDict:
+ inputDict['responsesLeft'] -= 1
+
+class ExplodeLightSystem(LightSystem):
+ def respond(self, location, data):
+ if data['responsesLeft'] != 0:
+ data['responsesLeft'] -= 1
+ for i in range(data['responsesLeft']):
+ LightSystem.respond(self, (location[0]+random.randint(-50,50),
+ location[1]+random.randint(-5,5)), dict(data))
+ def respondToInput(self, inputDict):
+ LightSystem.respond(self, inputDict['mouse'], {'responsesLeft':5})
+pygame.init()
+screen = pygame.display.set_mode((1300,50))
+background = pygame.Surface(screen.get_size())
+background = background.convert()
+background.fill(Color('Black'))
+clock = pygame.time.Clock()
+l = BouncyLightSystemMultiple(1300, 50)
+#l.respond((0, 25), None)
+#l.allOn()
+while 1:
+ for event in pygame.event.get():
+ if event.type is MOUSEBUTTONDOWN:
+ l.respondToInput({'mouse': pygame.mouse.get_pos()})
+ clock.tick(10)
+ background.fill(Color('Black'))
+ l.render(background)
+
+ screen.blit(background, (0,0))
+ pygame.display.flip()
+ l.timeStep()
+
+
+
+