From b335b746523ffd59db1402b097a802b3fd99eaac Mon Sep 17 00:00:00 2001 From: rcoh Date: Wed, 8 Dec 2010 16:39:50 -0500 Subject: Code for the demo. Everything works afaik. Contains a couple more optimizations. Contains modify param behavior. Improved support for recursive hooks. Modifications to SmootCoreObject to get us closer to a fully multi-threaded system. This should be the last commit directly to master. All further commits should be on subranches and get merged. --- LightInstallation.py | 2 +- Util.py | 12 +- behaviors/BehaviorChain.py | 11 +- behaviors/ColorChangerBehavior.py | 5 +- behaviors/ModifyParam.py | 27 ++++ behaviors/RecursiveDecay.py | 4 +- behaviors/RunningBehavior.py | 6 +- config/LightInstallationConfig.xml | 18 +-- config/Outdoor.xml | 252 +++++++++++++++++++++++++++++++++++++ inputs/PygameInput.py | 1 + operationscore/Behavior.py | 2 +- operationscore/Input.py | 15 +-- operationscore/Renderer.py | 2 + operationscore/SmootCoreObject.py | 14 ++- pixelcore/Pixel.py | 1 + pixelcore/Screen.py | 4 +- pixelevents/DecayEvent.py | 1 - pixelmappers/GaussianMapper.py | 5 +- 18 files changed, 337 insertions(+), 45 deletions(-) create mode 100644 behaviors/ModifyParam.py create mode 100644 config/Outdoor.xml diff --git a/LightInstallation.py b/LightInstallation.py index 8f71cf0..e4adab7 100644 --- a/LightInstallation.py +++ b/LightInstallation.py @@ -87,7 +87,7 @@ class LightInstallation: #self.screen.allOn() lastLoopTime = Util.time() refreshInterval = 30 - runCount = 1000 + runCount = 10000 while runCount > 0: runCount -= 1 loopStart = Util.time() diff --git a/Util.py b/Util.py index ea75b05..07ce545 100644 --- a/Util.py +++ b/Util.py @@ -17,6 +17,7 @@ MAGICHASH = 0x69000420 PORTOUT = 0x0108 classArgsMem = {} UNI = 0 +colorByteMem = {} CONFIG_PATH = 'config/' kinetDict = {'flags': 0, 'startcode': 0, 'pad':0} componentDict = {} @@ -75,6 +76,7 @@ def loadConfigFile(fileName): def fileToDict(fileName): fileText = '' try: + print 'File Read' with open(fileName) as f: for line in f: fileText += line.rstrip('\n').lstrip('\t') + ' ' @@ -95,7 +97,7 @@ def safeColor(c): def combineColors(c1,c2): return safeColor([c1[i]+c2[i] for i in range(min(len(c1),len(c2)))]) def multiplyColor(color, percent): - return safeColor(tuple([channel*(percent) for channel in color])) + return safeColor([channel*(percent) for channel in color]) #parses arguments into python objects if possible, otherwise leaves as strings def generateArgDict(parentNode, recurse=False): args = {} @@ -159,13 +161,17 @@ def composePixelStripData(pixelStrip): for channel in color: #skip the last value, its an #alpha value packet.append(struct.pack('B', channel)) - #pdb.set_trace() return packet +# packet = [0]*len(pixelStrip.pixels)*3 #preallocate for speed +# for i in range(len(pixelStrip.pixels)): +#color = pixelStrip.pixels[i].state() +#packet[i:i+2] = color +# return bytearray(packet) def composePixelStripPacket(pixelStrip,port): packet = bytearray() data = composePixelStripData(pixelStrip) subDict = dict(kinetDict) - subDict['len'] = 38399 #I have no idea why this works. + subDict['len'] = 38000 #I have no idea why this works. subDict['port'] = port #pdb.set_trace() packet.extend(kinetPortOutPacket(subDict)) diff --git a/behaviors/BehaviorChain.py b/behaviors/BehaviorChain.py index 8b5cb1d..fe50573 100644 --- a/behaviors/BehaviorChain.py +++ b/behaviors/BehaviorChain.py @@ -20,8 +20,11 @@ class BehaviorChain(Behavior): if behaviorId in self.hooks: #process recursive hook if there is one hookBehavior = Util.getComponentById(self.hooks[behaviorId]) - (response, recurrence) = \ - hookBehavior.immediateProcessInput(response, - recurrence) - self.feedback[behaviorId] = recurrence +#we feed its recurrence in as input to the behavior. + (recurrence, hookRecurrence) = \ + hookBehavior.immediateProcessInput(recurrence, \ + []) + if hookRecurrence != []: + print 'Hook recurrences are not currently supported. Implement it yourself or bug russell' + self.feedback[behaviorId] = recurrence return response diff --git a/behaviors/ColorChangerBehavior.py b/behaviors/ColorChangerBehavior.py index 12ef6f4..a3b1739 100644 --- a/behaviors/ColorChangerBehavior.py +++ b/behaviors/ColorChangerBehavior.py @@ -7,8 +7,9 @@ class ColorChangerBehavior(Behavior): for sensory in sensorInputs: newDict = dict(sensory) #don't run into shallow copy issues if self['ColorList'] != None: - newDict['Color'] = Util.chooseRandomColor(self['ColorList']) + newDict['Color'] = Util.chooseRandomColor(self['ColorList']) #TODO: this doesn't work. else: newDict['Color'] = Util.randomColor() +#newDict['Color'] = (255,0,0) ret.append(newDict) - return ret + return (ret, recursiveInputs) diff --git a/behaviors/ModifyParam.py b/behaviors/ModifyParam.py new file mode 100644 index 0000000..38b8cd5 --- /dev/null +++ b/behaviors/ModifyParam.py @@ -0,0 +1,27 @@ +from operationscore.Behavior import * +import Util +import pdb +#Class to perform a given operation on some element of an argDict. Designed to be used a recursive hook, but can serve sensor-based functions as well. Specify ParamType (Sensor or Recurse), ParamName, and ParamOp, (a valid python statement with the old value represented as {val}) +class ModifyParam(Behavior): + def processResponse(self, sensorInputs, recursiveInputs): + paramType = self['ParamType'] + paramName = self['ParamName'] + paramOp = self['ParamOp'] + if paramType == 'Sensor': + searchSet = sensorInputs + elif paramType == 'Recurse': + searchSet = recursiveInputs + else: + raise Exception('Unknown Param Type') + for behaviorInput in searchSet: + if paramName in behaviorInput: + try: + paramOp = paramOp.replace('{val}', 'behaviorInput[paramName]') #convert the {val} marker to something we can execute + behaviorInput[paramName] = eval(paramOp) + except: + raise Exception('Bad operation. Use things like \'{val}*5\', \'{val}+5\', exp({val}) etc.') + if paramType == 'Sensor': #return accordingly + return (searchSet, recursiveInputs) + if paramType == 'Recurse': + return (sensorInputs, searchSet) + diff --git a/behaviors/RecursiveDecay.py b/behaviors/RecursiveDecay.py index b119b85..ee38bc4 100644 --- a/behaviors/RecursiveDecay.py +++ b/behaviors/RecursiveDecay.py @@ -2,11 +2,11 @@ from operationscore.Behavior import * class RecursiveDecay(Behavior): def processResponse(self, sensorInputs, recursiveInputs): ret = [] - for response in recursiveInputs: + for response in sensorInputs: if not 'ResponsesLeft' in response: response['ResponsesLeft'] = self['InitialResponseCount'] else: response['ResponsesLeft'] -= 1 if response['ResponsesLeft'] > 0: ret.append(response) - return (sensorInputs, ret) #no direct ouput + return (ret, recursiveInputs) #no direct ouput diff --git a/behaviors/RunningBehavior.py b/behaviors/RunningBehavior.py index 255ce69..3d82d29 100644 --- a/behaviors/RunningBehavior.py +++ b/behaviors/RunningBehavior.py @@ -10,11 +10,13 @@ class RunningBehavior(Behavior): outDict = dict(recurInput) if not 'Dir' in outDict: outDict['Dir'] = 1 #to the right + if not 'StepSize' in outDict: + outDict['StepSize'] = self['StepSize'] outDict['Location']= Util.addLocations(outDict['Location'], - (self['StepSize']*outDict['Dir'],0)) + (outDict['StepSize']*outDict['Dir'],0)) if not Util.pointWithinBoundingBox(outDict['Location'], \ Util.getScreen().getSize()): - outDict['Dir'] *= -1 + outDict['Dir'] *= -1 ret.append(outDict) ret += newResponses return (ret, ret) diff --git a/config/LightInstallationConfig.xml b/config/LightInstallationConfig.xml index dfa05d7..5067335 100644 --- a/config/LightInstallationConfig.xml +++ b/config/LightInstallationConfig.xml @@ -7,13 +7,13 @@ strip1 - 25 + 250 X -1 12 - 12 - 50 + 500 (10,20) @@ -21,12 +21,12 @@ layouts.ZigzagLayout strip2 - 25 + 250 X 1 12 - 12 - 50 + 5 + 500 (10,30) @@ -44,8 +44,8 @@ pixelmappers.GaussianMapper gaussmap - 30 - 5 + 20 + 3 1 @@ -121,7 +121,7 @@ decay Exponential - .001 + .01 0 False diff --git a/config/Outdoor.xml b/config/Outdoor.xml new file mode 100644 index 0000000..7f054f0 --- /dev/null +++ b/config/Outdoor.xml @@ -0,0 +1,252 @@ + + + + + layouts.LineLayout + + strip1 + 4 + 4 + 50 + (10,20) + + + + layouts.LineLayout + + strip2 + 4 + 4 + 50 + (10,24) + + + + layouts.LineLayout + + strip3 + 4 + 4 + 50 + (10,28) + + + + layouts.LineLayout + + strip4 + 4 + 4 + 50 + (10,32) + + + + layouts.LineLayout + + strip5 + 4 + 4 + 50 + (10,36) + + + + layouts.LineLayout + + strip6 + 4 + 4 + 50 + (10,40) + + + + + + + pixelmappers.GaussianMapper + + gaussmap + 20 + 3 + 1 + + + + + + renderers.PygameRenderer + + pygamerender + (1300,50) + + + + renderers.IndoorRenderer + + indoorRenderer + + 10.31.255.233 + {'strip1':1, 'strip2':2} + + + 10.32.97.17 + {'strip3':1, 'strip4':2} + + + 10.32.96.211 + {'strip5':1, 'strip6':2} + + + + + + + inputs.PygameInput + + pygame + 100 + + + + inputs.PygameInput + + followmouse + 10 + True + + + + inputs.UDPInput + + UDP + 6038 + 100 + + + + + + behaviors.EchoBehavior + + echo + 0 + False + + + + + + behaviors.ColorChangerBehavior + + colorchange + 0 + False + + + pygame + + + + + behaviors.DecayBehavior + + decay + Exponential + .01 + 0 + False + + + + + + behaviors.DebugBehavior + + debug + 0 + + pygame + + + + + behaviors.RecursiveDecay + + recursivedecay + 70 + + + + behaviors.BehaviorChain + + runcolordecay + + pygame + + + colorchange + running + decay + + {'running':'acceleratedie'} + True + + + + behaviors.ModifyParam + + accelerate + Sensor + StepSize + {val}*1.05 + + + + behaviors.BehaviorChain + + acceleratedie + + accelerate + recursivedecay + + + + + behaviors.BehaviorChain + + mousechaser + + followmouse + + + echo + colorchange + decay + + True + + + + behaviors.RunningBehavior + + running + + pygame + + 1 + False + + + + diff --git a/inputs/PygameInput.py b/inputs/PygameInput.py index e07592b..1f438d6 100644 --- a/inputs/PygameInput.py +++ b/inputs/PygameInput.py @@ -9,6 +9,7 @@ class PygameInput(Input): #try: if self['FollowMouse']: self.respond({Util.location: pygame.mouse.get_pos()}) + return for event in pygame.event.get(): if event.type is KEYDOWN: self.respond({Util.location: (5,5),'Key': event.key}) diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py index 83d2e7d..3bdf1ec 100644 --- a/operationscore/Behavior.py +++ b/operationscore/Behavior.py @@ -41,7 +41,7 @@ class Behavior(SmootCoreObject): [self.addInput(sensorInput) for sensorInput in sensorInputs] else: self.addInput(sensorInputs) - def timeStep(self): + def timeStep(self): #TODO: type checking. clean this up responses = self.processResponse(self.sensorResponseQueue, \ self.recursiveResponseQueue) if type(responses) == type(tuple()) and len(responses) == 2: diff --git a/operationscore/Input.py b/operationscore/Input.py index 9ee59f8..2144678 100644 --- a/operationscore/Input.py +++ b/operationscore/Input.py @@ -1,4 +1,5 @@ import threading,time,Util +from operationscore.SmootCoreObject import * #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 @@ -6,7 +7,7 @@ import threading,time,Util #Inheriting classes MAY define inputInit. This is called before the loop #begins. import pdb -class Input(threading.Thread): +class Input(SmootCoreObject): #Event scope is a function pointer the function that will get called when #an Parent is raised. def __init__(self, argDict): @@ -19,16 +20,6 @@ class Input(threading.Thread): self.inputInit() threading.Thread.__init__(self) self.daemon = True #This kills this thread when the main thread stops - #CHEATING until I can get multiple inheritence working - 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 respond(self, eventDict): #if eventDict != []: #pdb.set_trace() @@ -48,7 +39,9 @@ class Input(threading.Thread): def run(self): while self.parentAlive(): time.sleep(self.argDict['RefreshInterval']/float(1000)) + self.acquireLock() self.sensingLoop() + self.releaseLock() def sensingLoop(self): pass def inputInit(self): diff --git a/operationscore/Renderer.py b/operationscore/Renderer.py index 11fd8ca..8e31f8b 100644 --- a/operationscore/Renderer.py +++ b/operationscore/Renderer.py @@ -2,10 +2,12 @@ #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. +#TODO: multithreaded-rendering from operationscore.SmootCoreObject import * class Renderer(SmootCoreObject): def init(self): self.initRenderer() + threading.Thread.__init__(self) def render(lightSystem): pass def initRenderer(self): diff --git a/operationscore/SmootCoreObject.py b/operationscore/SmootCoreObject.py index d29e710..8514b3e 100644 --- a/operationscore/SmootCoreObject.py +++ b/operationscore/SmootCoreObject.py @@ -1,16 +1,24 @@ import Util import pdb -class SmootCoreObject: - def __init__(self, argDict): +import threading +import thread +class SmootCoreObject(threading.Thread): + def __init__(self, argDict, skipValidation = False): self.argDict = argDict self.validateArgs(self.className()+'.params') + self.lock = thread.allocate_lock() self.init() #call init of inheriting class # self.__setitem__ = self.argDict.__setitem__ # self.__getitem__ = self.argDict.__getitem__ def init(self): pass + def acquireLock(self): + self.lock = thread.allocate_lock() #TODO: fix. + self.lock.acquire() + def releaseLock(self): + self.lock.release() def className(self): - return str(self.__class__).split('.')[-1] + return str(self.__class__).split('.')[-1] #TODO: this doesn't work. def __setitem__(self,k, item): self.argDict[k] = item def __getitem__(self, item): diff --git a/pixelcore/Pixel.py b/pixelcore/Pixel.py index 4c8ec89..ba87dff 100644 --- a/pixelcore/Pixel.py +++ b/pixelcore/Pixel.py @@ -47,6 +47,7 @@ class Pixel: else: deadEvents.append(eventTime) [self.events.pop(event) for event in deadEvents] + resultingColor = [int(round(c)) for c in resultingColor] self.memState = tuple(resultingColor) return tuple(resultingColor) def __str__(self): diff --git a/pixelcore/Screen.py b/pixelcore/Screen.py index 71b9b0b..92805a8 100644 --- a/pixelcore/Screen.py +++ b/pixelcore/Screen.py @@ -59,9 +59,9 @@ class Screen: minY = min(y, minY) maxY = max(y, maxY) - self.size = (minX, minY, maxX, maxY) + self.size = (0,0, maxX, maxY) self.sizeValid = True - return (minX, minY, maxX, maxY) + return (0, 0, maxX+100, maxY+100) #TODO: cleaner #private def processResponse(self, responseInfo): #we need to make a new dict for #each to prevent interference diff --git a/pixelevents/DecayEvent.py b/pixelevents/DecayEvent.py index 01255be..9a7c600 100644 --- a/pixelevents/DecayEvent.py +++ b/pixelevents/DecayEvent.py @@ -2,7 +2,6 @@ from operationscore.PixelEvent import * import Util, math class DecayEvent(PixelEvent): def initEvent(self): - self.validateArgs('DecayEvent.params') self['Coefficient'] = abs(self['Coefficient']) def state(self,timeDelay): if self['DecayType'] == 'Exponential': diff --git a/pixelmappers/GaussianMapper.py b/pixelmappers/GaussianMapper.py index 1cf9e88..04bd447 100644 --- a/pixelmappers/GaussianMapper.py +++ b/pixelmappers/GaussianMapper.py @@ -2,15 +2,12 @@ from operationscore.PixelMapper import * import Util class GaussianMapper(PixelMapper): def mappingFunction(self, eventLocation, screen): - returnPixels = [] + returnPixels = [] #TODO: consider preallocation and trimming [x,y] = eventLocation for (x,pixel) in screen.pixelsInRange(x-self['CutoffDist'], \ x+self['CutoffDist']): pixelDist = Util.dist(pixel.location, eventLocation) if pixelDist < self['CutoffDist']: w = Util.gaussian(pixelDist, self['Height'], 0, self['Width']) - if w>1: - #pdb.set_trace() - pass returnPixels.append((pixel, w)) return returnPixels -- cgit v1.2.3