diff options
author | Russell Cohen <rcoh@mit.edu> | 2010-11-23 00:24:08 -0500 |
---|---|---|
committer | Russell Cohen <rcoh@mit.edu> | 2010-11-23 00:24:08 -0500 |
commit | 99325eca1bbe7adc532a63fb0ec06e593b4f9b39 (patch) | |
tree | 251a2d1c6ea4814747bf7bc1c3174129198e9bfb | |
parent | a9b3325a9171abd0785bd03cbcb51cbbf47da1f2 (diff) |
First Commit with the code. In process of doing some refactoring.
-rw-r--r-- | Behavior.py | 33 | ||||
-rw-r--r-- | DebugBehavior.py | 8 | ||||
-rw-r--r-- | DecayEvent.py | 12 | ||||
-rw-r--r-- | EchoBehavior.py | 12 | ||||
-rw-r--r-- | IndoorRenderer.py | 31 | ||||
-rw-r--r-- | Input.py | 46 | ||||
-rw-r--r-- | LayoutEngine.py | 30 | ||||
-rw-r--r-- | Light.py | 51 | ||||
-rw-r--r-- | LightInstallation.py | 91 | ||||
-rw-r--r-- | LightStrip.py | 42 | ||||
-rw-r--r-- | LightSystem.py | 30 | ||||
-rw-r--r-- | LineLayout.py | 5 | ||||
-rw-r--r-- | PixelEvent.py | 13 | ||||
-rw-r--r-- | PixelStrip.py | 42 | ||||
-rw-r--r-- | PygameInput.py | 17 | ||||
-rw-r--r-- | PygameRenderer.py | 20 | ||||
-rw-r--r-- | Renderer.py | 12 | ||||
-rw-r--r-- | Screen.py | 31 | ||||
-rw-r--r-- | SmootCoreObject.py | 23 | ||||
-rw-r--r-- | StepEvent.py | 14 | ||||
-rw-r--r-- | TCPInput.py | 51 | ||||
-rw-r--r-- | UDPInput.py | 16 | ||||
-rw-r--r-- | Util.py | 118 | ||||
-rw-r--r-- | ZigzagLayout.py | 45 | ||||
-rw-r--r-- | gfxdemo.py | 82 |
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) + @@ -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() + + + + |