diff options
40 files changed, 381 insertions, 152 deletions
diff --git a/LightInstallation.py b/LightInstallation.py index 8b2d059..db7b808 100644 --- a/LightInstallation.py +++ b/LightInstallation.py @@ -88,11 +88,7 @@ class LightInstallation: def initializeComponent(self, config): components = [] if config != None: - config = configGetter.resolveConfigInheritance(config) for configItem in config.getchildren(): - configItem = configGetter.resolveConfigInheritance(configItem) #resolve - #inheritences. TODO: migrate to a recursive inheritence resolver that gets run on - #file-parse try: [module,className] = configItem.find('Class').text.split('.') except: @@ -102,11 +98,12 @@ class LightInstallation: try: exec('from ' + module+'.'+className + ' import *') main_log.debug(module +'.' +className + 'imported') - except: + except Exception as inst: + pdb.set_trace() main_log.error('Error importing ' + module+'.'+'.className. Component not\ initialized.') continue #TODO: verify functions as expected - args = configGetter.generateArgDict(configItem.find('Args')) + args = configGetter.pullArgsFromItem(configItem) args['parentScope'] = self #TODO: we shouldn't give away scope #like this, find another way. try: @@ -124,7 +121,7 @@ class LightInstallation: #self.screen.allOn() lastLoopTime = clock.time() refreshInterval = 30 - runCount = 10000 + runCount = 3000 while runCount > 0: runCount -= 1 loopStart = clock.time() diff --git a/Profile.py b/Profile.py new file mode 100644 index 0000000..45ccc35 --- /dev/null +++ b/Profile.py @@ -0,0 +1,4 @@ +import cProfile +from LightInstallation import main +command = """main(['', 'config/Outdoor.xml'])""" +cProfile.runctx(command, globals(), locals(), filename="smootlight.profile") diff --git a/ResponseMover.py b/ResponseMover.py new file mode 100644 index 0000000..718400d --- /dev/null +++ b/ResponseMover.py @@ -0,0 +1,15 @@ +from operationscore.Behavior import * +import util.ComponentRegistry as compReg +#ResponseMover is a scaffold for behaviors that spawn 'walkers' which act autonomously on input. +#Add a recursive hook to control the movement. +class ResponseMover(Behavior): + def processResponse(self, sensorInputs, recursiveInputs): + newResponses = sensorInputs + ret = [] + ret += newResponses + for recurInput in recursiveInputs: + outDict = dict(recurInput) + ret.append(outDict) + ret += newResponses + return (ret, ret) + diff --git a/TestProfile.py b/TestProfile.py new file mode 100644 index 0000000..eccb884 --- /dev/null +++ b/TestProfile.py @@ -0,0 +1,34 @@ +import cProfile +from LightInstallation import main +numiter = 10000000 +def main1(): + for i in xrange(0,numiter): + if 'abc' == 'def': + pass + if 'abc' == 'abc': + pass + +def main2(): + for i in xrange(0,numiter): + if 1 == 2: + pass + if 1 == 1: + pass + +x = [1,2,3] +a = [] +def abc1(): + for i in xrange(0,numiter): + a = min(4, 255) + b = min(257, 255) + +def abc2(): + for i in xrange(0,numiter): + a = 4 if 4 < 255 else 255 + b = 257 if 257 < 255 else 255 +command = """abc1()""" +cProfile.runctx(command, globals(), locals()) + +command = """abc2()""" +cProfile.runctx(command, globals(), locals()) + diff --git a/behaviors/Accelerate.xml b/behaviors/Accelerate.xml new file mode 100644 index 0000000..2a3d7ac --- /dev/null +++ b/behaviors/Accelerate.xml @@ -0,0 +1,8 @@ +<Behavior> + <Class>behaviors.ModifyParam</Class> + <Args> + <ParamType>Sensor</ParamType> + <ParamName>StepSize</ParamName> + <ParamOp>{val}*1.05</ParamOp> + </Args> +</Behavior> diff --git a/behaviors/LoopAndDie.xml b/behaviors/LoopAndDie.xml new file mode 100644 index 0000000..af26562 --- /dev/null +++ b/behaviors/LoopAndDie.xml @@ -0,0 +1,6 @@ +<Behavior> + <Class>behaviors.RecursiveDecay</Class> + <Args> + <InitialResponseCount>70</InitialResponseCount> + </Args> +</Behavior> diff --git a/behaviors/PixelDecay.xml b/behaviors/PixelDecay.xml new file mode 100644 index 0000000..f9eee0d --- /dev/null +++ b/behaviors/PixelDecay.xml @@ -0,0 +1,9 @@ +<Behavior> + <Class>behaviors.DecayBehavior</Class> + <Args> + <DecayType>Exponential</DecayType> + <Coefficient>.01</Coefficient> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + </Args> +</Behavior> diff --git a/behaviors/RandomColor.xml b/behaviors/RandomColor.xml new file mode 100644 index 0000000..afac09c --- /dev/null +++ b/behaviors/RandomColor.xml @@ -0,0 +1,8 @@ +<Behavior> + <Class>behaviors.ColorChangerBehavior</Class> + <Args> + <Id>colorchange</Id> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + </Args> +</Behavior> diff --git a/behaviors/RandomWalk.py b/behaviors/RandomWalk.py new file mode 100644 index 0000000..8254430 --- /dev/null +++ b/behaviors/RandomWalk.py @@ -0,0 +1,5 @@ +from operationscore.Behavior import * +import util.ComponentRegistry as compReg +class RandomWalk(Behavior): + def processResponse(self, sensors, recursives): + diff --git a/behaviors/RunningBehavior.xml b/behaviors/RunningBehavior.xml new file mode 100644 index 0000000..2a7bf37 --- /dev/null +++ b/behaviors/RunningBehavior.xml @@ -0,0 +1,8 @@ +<Behavior> + <Class>behaviors.RunningBehavior</Class> + <Args> + <Id>running</Id> + <StepSize>1</StepSize> + <RenderToScreen>False</RenderToScreen> + </Args> +</Behavior> diff --git a/config/Outdoor.xml b/config/Outdoor.xml index 65df4d0..9ca33e9 100644 --- a/config/Outdoor.xml +++ b/config/Outdoor.xml @@ -20,13 +20,19 @@ <Args> <Id>gaussmap</Id> <CutoffDist>20</CutoffDist> - <Width>1</Width> + <MinWeight>0.01</MinWeight> + <Width>3</Width> <Height>1</Height> </Args> </PixelMapper> </PixelMapperConfiguration> <RendererConfiguration> - <InheritsFrom>renderers/SixStripUDPPygame.xml</InheritsFrom> + <Renderer> + <InheritsFrom>renderers/SixStripUDP.xml</InheritsFrom> + </Renderer> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> </RendererConfiguration> <InputConfiguration> <InputElement> @@ -36,9 +42,15 @@ <RefreshInterval>100</RefreshInterval> </Args> </InputElement> - <InputElement> + <InputElement Id="followmouse"> <InheritsFrom>inputs/MouseFollower.xml</InheritsFrom> </InputElement> + <InputElement> + <Class>inputs.RandomLocs</Class> + <Args> + <Id>randomLoc</Id> + </Args> + </InputElement> </InputConfiguration> <BehaviorConfiguration> <Behavior> @@ -49,29 +61,11 @@ <RenderToScreen>False</RenderToScreen> </Args> </Behavior> - <Behavior> - <Class>behaviors.ColorChangerBehavior</Class> - <Args> - <Id>colorchange</Id> - <z-index>0</z-index> - <RenderToScreen>False</RenderToScreen> - <!--<ColorList> - <Color>(255,0,0)</Color> - </ColorList>--> - <Inputs> - <Id>pygame</Id> - </Inputs> - </Args> + <Behavior Id="colorchange"> + <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom> </Behavior> - <Behavior> - <Class>behaviors.DecayBehavior</Class> - <Args> - <Id>decay</Id> - <DecayType>Exponential</DecayType> - <Coefficient>.01</Coefficient> - <z-index>0</z-index> - <RenderToScreen>False</RenderToScreen> - </Args> + <Behavior Id="decay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> </Behavior> <Behavior> <Class>behaviors.DebugBehavior</Class> @@ -89,12 +83,8 @@ <Id>pixelsleft</Id> </Args> </Behavior> - <Behavior> - <Class>behaviors.RecursiveDecay</Class> - <Args> - <Id>recursivedecay</Id> - <InitialResponseCount>700</InitialResponseCount> - </Args> + <Behavior Id="recursivedecay"> + <InheritsFrom>behaviors/LoopAndDie.xml</InheritsFrom> </Behavior> <Behavior> <Class>behaviors.BehaviorChain</Class> @@ -102,6 +92,7 @@ <Id>runcolordecay</Id> <Inputs> <Id>pygame</Id> + <Id>randomLoc</Id> </Inputs> <ChainedBehaviors> <Id>colorchange</Id> @@ -110,17 +101,11 @@ </ChainedBehaviors> <RecursiveHooks>{'running':'acceleratedie'}</RecursiveHooks> <RenderToScreen>True</RenderToScreen> - <!--<Mapper>gaussmap</Mapper>--> + <Mapper>gaussmap</Mapper> </Args> </Behavior> - <Behavior> - <Class>behaviors.ModifyParam</Class> - <Args> - <Id>accelerate</Id> - <ParamType>Sensor</ParamType> - <ParamName>StepSize</ParamName> - <ParamOp>{val}*1.01</ParamOp> - </Args> + <Behavior Id="accelerate"> + <InheritsFrom>behaviors/Accelerate.xml</InheritsFrom> </Behavior> <Behavior> <Class>behaviors.BehaviorChain</Class> @@ -144,19 +129,11 @@ <Id>pixelsleft</Id> <Id>decay</Id> </ChainedBehaviors> - <RenderToScreen>True</RenderToScreen> - </Args> - </Behavior> - <Behavior> - <Class>behaviors.RunningBehavior</Class> - <Args> - <Id>running</Id> - <Inputs> - <Id>pygame</Id> - </Inputs> - <StepSize>1</StepSize> <RenderToScreen>False</RenderToScreen> </Args> </Behavior> + <Behavior Id="running"> + <InheritsFrom>behaviors/RunningBehavior.xml</InheritsFrom> + </Behavior> </BehaviorConfiguration> </LightInstallation> diff --git a/inputs/PygameInput.py b/inputs/PygameInput.py index 6779a20..a39c089 100644 --- a/inputs/PygameInput.py +++ b/inputs/PygameInput.py @@ -13,6 +13,8 @@ class PygameInput(Input): return for event in pygame.event.get(): if event.type is KEYDOWN: + if event.key == 301: + exit() self.respond({Strings.LOCATION: (5,5),'Key': event.key}) if event.type is MOUSEBUTTONDOWN: self.respond({Strings.LOCATION: pygame.mouse.get_pos()}) diff --git a/inputs/RandomLocs.py b/inputs/RandomLocs.py new file mode 100644 index 0000000..d1ce1c7 --- /dev/null +++ b/inputs/RandomLocs.py @@ -0,0 +1,13 @@ +import util.TimeOps as clock +import random +import util.Geo as Geo +import util.Strings as Strings +from operationscore.Input import * +class RandomLocs(Input): + def inputInit(self): + self['LastEvent'] = clock.time() + def sensingLoop(self): #TODO: move to params + currentTime = clock.time() + if currentTime - self['LastEvent'] > 2000: + self.respond({Strings.LOCATION: Geo.randomLoc((50,50))}) + self['LastEvent'] = currentTime diff --git a/layouts/50PixelStrip.xml b/layouts/50PixelStrip.xml new file mode 100644 index 0000000..beabb97 --- /dev/null +++ b/layouts/50PixelStrip.xml @@ -0,0 +1,8 @@ +<PixelStrip> + <Class>layouts.LineLayout</Class> + <Args> + <pixelToPixelSpacing>4</pixelToPixelSpacing> + <spacing>4</spacing> + <numPixels>50</numPixels> + </Args> +</PixelStrip> diff --git a/layouts/BasicSixStrip.xml b/layouts/BasicSixStrip.xml index 38c50d4..0d2d277 100644 --- a/layouts/BasicSixStrip.xml +++ b/layouts/BasicSixStrip.xml @@ -1,61 +1,43 @@ <PixelConfiguration> <PixelStrip> - <Class>layouts.LineLayout</Class> - <Args> + <InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom> + <Args OverrideBehavior="Merge"> <Id>strip1</Id> - <pixelToPixelSpacing>4</pixelToPixelSpacing> - <spacing>4</spacing> - <numPixels>50</numPixels> <originLocation>(10,20)</originLocation> </Args> </PixelStrip> <PixelStrip> - <Class>layouts.LineLayout</Class> - <Args> + <InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom> + <Args OverrideBehavior="Merge"> <Id>strip2</Id> - <pixelToPixelSpacing>4</pixelToPixelSpacing> - <spacing>4</spacing> - <numPixels>50</numPixels> <originLocation>(10,24)</originLocation> </Args> </PixelStrip> <PixelStrip> - <Class>layouts.LineLayout</Class> - <Args> + <InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom> + <Args OverrideBehavior="Merge"> <Id>strip3</Id> - <pixelToPixelSpacing>4</pixelToPixelSpacing> - <spacing>4</spacing> - <numPixels>50</numPixels> <originLocation>(10,28)</originLocation> </Args> </PixelStrip> <PixelStrip> - <Class>layouts.LineLayout</Class> - <Args> + <InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom> + <Args OverrideBehavior="Merge"> <Id>strip4</Id> - <pixelToPixelSpacing>4</pixelToPixelSpacing> - <spacing>4</spacing> - <numPixels>50</numPixels> <originLocation>(10,32)</originLocation> </Args> </PixelStrip> <PixelStrip> - <Class>layouts.LineLayout</Class> - <Args> + <InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom> + <Args OverrideBehavior="Merge"> <Id>strip5</Id> - <pixelToPixelSpacing>4</pixelToPixelSpacing> - <spacing>4</spacing> - <numPixels>50</numPixels> <originLocation>(10,36)</originLocation> </Args> </PixelStrip> <PixelStrip> - <Class>layouts.LineLayout</Class> - <Args> + <InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom> + <Args OverrideBehavior="Merge"> <Id>strip6</Id> - <pixelToPixelSpacing>4</pixelToPixelSpacing> - <spacing>4</spacing> - <numPixels>50</numPixels> <originLocation>(10,40)</originLocation> </Args> </PixelStrip> diff --git a/logger/loggingConfig.ini b/logger/loggingConfig.ini index ee35749..6727c26 100644 --- a/logger/loggingConfig.ini +++ b/logger/loggingConfig.ini @@ -18,7 +18,7 @@ level = INFO handlers = console [logger_smoot_light] -level = INFO +level = DEBUG handlers = file qualname = smoot_light propagate = 0 @@ -40,7 +40,7 @@ formatter = generic [handler_file] class = FileHandler args = ('/var/log/smoot_light/main.log', 'a') -level = INFO +level = DEBUG formatter = generic [handler_exception] diff --git a/operationscore/Input.py b/operationscore/Input.py index 9e8841a..3dd74cf 100644 --- a/operationscore/Input.py +++ b/operationscore/Input.py @@ -1,6 +1,6 @@ import threading,time -from operationscore.SmootCoreObject import * from logger import main_log, exception_log +from operationscore.ThreadedSmootCoreObject import ThreadedSmootCoreObject #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 @@ -8,7 +8,7 @@ from logger import main_log, exception_log #Inheriting classes MAY define inputInit. This is called before the loop #begins. import pdb -class Input(SmootCoreObject): +class Input(ThreadedSmootCoreObject): #Event scope is a function pointer the function that will get called when #an Parent is raised. def __init__(self, argDict): diff --git a/operationscore/Renderer.py b/operationscore/Renderer.py index 8e31f8b..88da606 100644 --- a/operationscore/Renderer.py +++ b/operationscore/Renderer.py @@ -3,8 +3,8 @@ #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): +from operationscore.ThreadedSmootCoreObject import * +class Renderer(ThreadedSmootCoreObject): def init(self): self.initRenderer() threading.Thread.__init__(self) diff --git a/operationscore/SmootCoreObject.py b/operationscore/SmootCoreObject.py index 26b3fc2..291519a 100644 --- a/operationscore/SmootCoreObject.py +++ b/operationscore/SmootCoreObject.py @@ -2,12 +2,11 @@ import pdb import threading import thread import util.Config as configGetter -class SmootCoreObject(threading.Thread): +class SmootCoreObject(object): def __init__(self, argDict, skipValidation = False): self.argDict = argDict self.validateArgs(self.className()+'.params') self.lock = thread.allocate_lock() - threading.Thread.__init__(self) self.init() #call init of inheriting class # self.__setitem__ = self.argDict.__setitem__ # self.__getitem__ = self.argDict.__getitem__ diff --git a/operationscore/ThreadedSmootCoreObject.py b/operationscore/ThreadedSmootCoreObject.py new file mode 100644 index 0000000..90611bc --- /dev/null +++ b/operationscore/ThreadedSmootCoreObject.py @@ -0,0 +1,14 @@ +import pdb +import threading +import thread +import util.Config as configGetter +from operationscore.SmootCoreObject import SmootCoreObject +class ThreadedSmootCoreObject(SmootCoreObject, threading.Thread): + def __init__(self, argDict, skipValidation = False): + self.argDict = argDict + self.validateArgs(self.className()+'.params') + self.lock = thread.allocate_lock() + threading.Thread.__init__(self) + self.init() #call init of inheriting class + # self.__setitem__ = self.argDict.__setitem__ + # self.__getitem__ = self.argDict.__getitem__ diff --git a/pixelcore/Pixel.py b/pixelcore/Pixel.py index f66d0bb..9f5cc85 100644 --- a/pixelcore/Pixel.py +++ b/pixelcore/Pixel.py @@ -1,7 +1,7 @@ import util.ColorOps as color import pdb from pixelevents.StepEvent import * -import util.TimeOps as clock +import util.TimeOps as timeops #Pixel keeps a queue of events (PixelEvent objects) (actually a dictionary #keyed by event time). Every time is state is #requested, it processes all the members of its queue. If a member returns none, @@ -10,12 +10,15 @@ import util.TimeOps as clock class Pixel: radius = 2 timeOff = -1 + def __init__(self, location): self.location = location self.events = {} self.memState = None + def turnOn(self): self.turnOnFor(-1) + #Turn the light white for 'time' ms. Really only meant for testing. Use #processInput instead. Also, you shouldn't use this anyway. You should be #using the input method on the screen! @@ -23,34 +26,42 @@ class Pixel: event = StepEvent.generate(time, (255,255,255)) #TODO: Move color to self.processInput(event, 0) #arg + #Add a pixelEvent to the list of active events def processInput(self,pixelEvent,zindex): #consider migrating arg to dict - self.events[clock.time()] = (zindex, pixelEvent) + self.events[timeops.time()] = (zindex, pixelEvent) + def clearAllEvents(self): self.events = {} + #Combines all PixelEvents currently active and computes the current color of #the pixel. def invalidateState(self): self.memState = None + def state(self): if self.memState != None: return self.memState - if len(self.events) == 0: + if self.events == []: return (0,0,0) deadEvents = [] - currentTime = clock.time() + currentTime = timeops.time() resultingColor = (0,0,0) + colors = [] for eventTime in self.events: #TODO: right color weighting code (zindex,event) = self.events[eventTime] eventResult = event.state(currentTime-eventTime) if eventResult != None: - resultingColor = color.combineColors(eventResult, resultingColor) + colors.append(eventResult) else: deadEvents.append(eventTime) + + resultingColor = color.combineColors(colors) [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): return 'Loc: ' + str(self.location) diff --git a/pixelcore/Screen.py b/pixelcore/Screen.py index da03ad2..b002896 100644 --- a/pixelcore/Screen.py +++ b/pixelcore/Screen.py @@ -6,6 +6,8 @@ import util.Search as Search import util.ComponentRegistry as compReg import util.Strings as Strings import itertools +import sys +from logger import main_log #Class representing a collection of Pixels grouped into PixelStrips. Needs a #PixelMapper, currently set via setMapper by may be migrated into the argDict. class Screen: @@ -15,28 +17,35 @@ class Screen: self.xSortedPixels = [] self.xPixelLocs = [] sizeValid = False + def addStrip(self, lS): self.pixelStrips.append(lS) self.sizeValid = False #keep track of whether or not our screen size has #been invalidated by adding more pixels self.computeXSortedPixels() + #Returns (pixelIndex, pixel). Does a binary search. def pixelsInRange(self, minX, maxX): minIndex = Search.find_ge(self.xPixelLocs, minX) maxIndex = Search.find_le(self.xPixelLocs, maxX)+1 return self.xSortedPixels[minIndex:maxIndex] + def computeXSortedPixels(self): for pixel in self: self.xSortedPixels.append((pixel.location[0], pixel)) self.xSortedPixels.sort() self.xPixelLocs = [p[0] for p in self.xSortedPixels] + 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 pixel strips chained togther return itertools.chain(*[strip.__iter__() for strip in \ self.pixelStrips]) #the * operator breaks the list into args + #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. @@ -46,14 +55,15 @@ class Screen: for response in tempQueue: self.processResponse(response) [p.invalidateState() for p in self] + #public def respond(self, responseInfo): self.responseQueue.append(responseInfo) + def getSize(self): if self.sizeValid: return self.size - (minX, minY, maxX, maxY) = (10**10,10**10,-10**10,-10*10) #TODO: don't - #be lazy + (minX, minY, maxX, maxY) = (sys.maxint,sys.maxint,-sys.maxint,-sys.maxint) for light in self: (x,y) = light.location @@ -65,6 +75,7 @@ class Screen: self.size = (0,0, maxX, maxY) self.sizeValid = True 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 @@ -78,6 +89,8 @@ class Screen: #if type(mapper) != type(PixelMapper): # raise Exception('No default mapper specified.') pixelWeightList = mapper.mapEvent(responseInfo['Location'], self) + main_log.debug(str(len(pixelWeightList))) + main_log.debug(pixelWeightList) PixelEvent.addPixelEventIfMissing(responseInfo) for (pixel, weight) in pixelWeightList: pixel.processInput(responseInfo['PixelEvent'].scale(weight), 0) #TODO: z-index diff --git a/pixelevents/DecayEvent.py b/pixelevents/DecayEvent.py index 7c1504a..c1166d6 100644 --- a/pixelevents/DecayEvent.py +++ b/pixelevents/DecayEvent.py @@ -3,14 +3,22 @@ import math from util.ColorOps import * class DecayEvent(PixelEvent): def initEvent(self): - self['Coefficient'] = abs(self['Coefficient']) - def state(self,timeDelay): #TODO: make this fast. + self.coefficient = float(abs(self['Coefficient'])) if self['DecayType'] == 'Exponential': - decay = math.exp(timeDelay*-1*self['Coefficient']) - if self['DecayType'] == 'Proportional': - decay = float(self['Coefficient']) / timeDelay - color = multiplyColor(self['Color'], decay) - return color if sum(color) > 5 else None + self.decayType = 1 + else: + self.decayType = 2 + self.color = self['Color'] + + #SUBVERTING DESIGN FOR THE SAKE OF EFFICIENCY -- RUSSELL COHEN (2011-01-03-23:18) + def state(self,timeDelay): + if self.decayType == 1: + decay = math.exp(timeDelay*-1*self.coefficient) + if self.decayType == 2: + decay = self.coefficient / timeDelay + color = multiplyColor(self.color, decay) + return color if (color[0] + color[1] + color[2]) > 5 else None + @staticmethod def generate(decayType, coefficient, color): args = {'DecayType': decayType, 'Coefficient':coefficient, 'Color':color} diff --git a/pixelmappers/GaussianMapper.py b/pixelmappers/GaussianMapper.py index 8755acf..8fdf16b 100644 --- a/pixelmappers/GaussianMapper.py +++ b/pixelmappers/GaussianMapper.py @@ -9,5 +9,6 @@ class GaussianMapper(PixelMapper): pixelDist = Geo.dist(pixel.location, eventLocation) if pixelDist < self['CutoffDist']: w = Geo.gaussian(pixelDist, self['Height'], 0, self['Width']) - returnPixels.append((pixel, w)) + if w > self['MinWeight']: + returnPixels.append((pixel, w)) return returnPixels diff --git a/pixelmappers/SimpleMapper.py b/pixelmappers/SimpleMapper.py index bc51cf9..5d4dceb 100644 --- a/pixelmappers/SimpleMapper.py +++ b/pixelmappers/SimpleMapper.py @@ -23,7 +23,7 @@ class SimpleMapper(PixelMapper): #optimizations possible. This might be slow in the long run if pixelValid: ret.append((pixel, 1)) - except: + except Exception as exp: raise Exception('Bad event condition') return ret diff --git a/renderers/IndoorRenderer.py b/renderers/IndoorRenderer.py index c13d11f..0ee566a 100644 --- a/renderers/IndoorRenderer.py +++ b/renderers/IndoorRenderer.py @@ -2,7 +2,7 @@ from operationscore.Renderer import * import util.PacketComposition as composer import util.NetworkOps as network import socket,pdb -port = 6038 +sock_port = 6038 #Renderer for a Specific Light System. class IndoorRenderer(Renderer): def initRenderer(self): @@ -26,9 +26,10 @@ class IndoorRenderer(Renderer): (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] = network.getConnectedSocket(ip,port) + self.sockets[ip] = network.getConnectedSocket(ip,sock_port) packet = composer.composePixelStripPacket(pixelStrip, port) self.sockets[ip].send(packet, 0x00) - except: - pass #Rendering error. Log it. LOG + #pdb.set_trace() + except Exception as inst: + print inst diff --git a/renderers/Pygame.xml b/renderers/Pygame.xml new file mode 100644 index 0000000..e37739d --- /dev/null +++ b/renderers/Pygame.xml @@ -0,0 +1,7 @@ +<Renderer> + <Class>renderers.PygameRenderer</Class> + <Args> + <Id>pygamerender</Id> + <displaySize>(1300,50)</displaySize> + </Args> +</Renderer> diff --git a/renderers/SixStripUDP.xml b/renderers/SixStripUDP.xml new file mode 100644 index 0000000..f3c9e75 --- /dev/null +++ b/renderers/SixStripUDP.xml @@ -0,0 +1,18 @@ +<Renderer> + <Class>renderers.IndoorRenderer</Class> + <Args> + <Id>indoorRenderer</Id> + <PowerSupply> + <IP>10.31.255.233</IP> + <PortMapping>{'strip1':1, 'strip2':2}</PortMapping> + </PowerSupply> + <PowerSupply> + <IP>10.32.97.17</IP> + <PortMapping>{'strip3':1, 'strip4':2}</PortMapping> + </PowerSupply> + <PowerSupply> + <IP>10.32.96.211</IP> + <PortMapping>{'strip5':1, 'strip6':2}</PortMapping> + </PowerSupply> + </Args> +</Renderer> diff --git a/tests/testdata/compositeTESTout.xml b/tests/testdata/compositeTESTout.xml index 435b75a..a306190 100644 --- a/tests/testdata/compositeTESTout.xml +++ b/tests/testdata/compositeTESTout.xml @@ -1,11 +1,11 @@ <Override> <A> - <override>overide parameter</override> + <blah>test</blah> + <foo>bar</foo> +<override>overide parameter</override> </A> -<B OverrideBehavior="Merge"> - <this>taht</this> - <that>this</that> -<SomeNewParam>blah</SomeNewParam> +<B OverrideBehavior="Replace"> + <SomeNewParam>blah</SomeNewParam> </B> <A> <someparam>appenedA</someparam> diff --git a/tests/testdata/compositeTRUTH.xml b/tests/testdata/compositeTRUTH.xml index 435b75a..a306190 100644 --- a/tests/testdata/compositeTRUTH.xml +++ b/tests/testdata/compositeTRUTH.xml @@ -1,11 +1,11 @@ <Override> <A> - <override>overide parameter</override> + <blah>test</blah> + <foo>bar</foo> +<override>overide parameter</override> </A> -<B OverrideBehavior="Merge"> - <this>taht</this> - <that>this</that> -<SomeNewParam>blah</SomeNewParam> +<B OverrideBehavior="Replace"> + <SomeNewParam>blah</SomeNewParam> </B> <A> <someparam>appenedA</someparam> diff --git a/tests/testdata/inheritanceTEST.xml b/tests/testdata/inheritanceTEST.xml index c2efd71..d90e9af 100644 --- a/tests/testdata/inheritanceTEST.xml +++ b/tests/testdata/inheritanceTEST.xml @@ -1,3 +1,22 @@ <A> - <InheritsFrom>tests/testdata/aParent.xml</InheritsFrom> + <B> + <SubB> + <SubSubB> + <A> + <InheritsFrom>tests/testdata/aParent.xml</InheritsFrom> + </A> + </SubSubB> + </SubB> + <A> + <InheritsFrom>tests/testdata/aParent.xml</InheritsFrom> + </A> + </B> </A> + +<!-- +<Parent> + <A> + <InheritsFrom>tests/testdata/aParent.xml</InheritsFrom> + </A> +</Parent> +--> diff --git a/tests/testdata/inheritanceTESTout.xml b/tests/testdata/inheritanceTESTout.xml index ffa6667..e8fb228 100644 --- a/tests/testdata/inheritanceTESTout.xml +++ b/tests/testdata/inheritanceTESTout.xml @@ -1,4 +1,16 @@ <A> - <param1>1</param1> + <B> + <SubB> + <SubSubB> + <A> + <param1>1</param1> <param2>2</param2> +</A> + </SubSubB> + </SubB> + <A> + <param1>1</param1> + <param2>2</param2> +</A> + </B> </A>
\ No newline at end of file diff --git a/tests/testdata/inheritanceTRUTH.xml b/tests/testdata/inheritanceTRUTH.xml index ffa6667..e8fb228 100644 --- a/tests/testdata/inheritanceTRUTH.xml +++ b/tests/testdata/inheritanceTRUTH.xml @@ -1,4 +1,16 @@ <A> - <param1>1</param1> + <B> + <SubB> + <SubSubB> + <A> + <param1>1</param1> <param2>2</param2> +</A> + </SubSubB> + </SubB> + <A> + <param1>1</param1> + <param2>2</param2> +</A> + </B> </A>
\ No newline at end of file diff --git a/tests/testdata/override.xml b/tests/testdata/override.xml index 1108fb0..722554a 100644 --- a/tests/testdata/override.xml +++ b/tests/testdata/override.xml @@ -2,7 +2,7 @@ <A> <override>overide parameter</override> </A> -<B OverrideBehavior="Merge"> +<B OverrideBehavior="Replace"> <SomeNewParam>blah</SomeNewParam> </B> <APPEND> diff --git a/util/ColorOps.py b/util/ColorOps.py index f1d3741..143444f 100644 --- a/util/ColorOps.py +++ b/util/ColorOps.py @@ -5,16 +5,16 @@ def randomColor(): def chooseRandomColor(colorList): return random.choice(colorList) def safeColor(c): - return [min(channel,255) for channel in c] -def combineColors(c1,c2): - return safeColor([c1[i]+c2[i] for i in range(min(len(c1),len(c2)))]) -def colorToInt(color): - return [int(channel) for channel in color] + c[0] = c[0] if c[0] < 255 else 255 + c[1] = c[1] if c[1] < 255 else 255 + c[2] = c[2] if c[2] < 255 else 255 + return c +def combineColors(colors): + result = [0,0,0] + for c in colors: + result[0] += c[0] + result[1] += c[1] + result[2] += c[2] + return safeColor(result) def multiplyColor(color, percent): - return safeColor([fastMultiply(channel, percent, 1) for channel in color]) - -def fastMultiply(value, mult, acc): - if type(mult) == type(int(5)): - return value*mult - return int(value)*int(mult*10**acc)*10**(-acc) - + return safeColor([channel*(percent) for channel in color]) diff --git a/util/Config.py b/util/Config.py index 040e9b0..4c1eb1e 100644 --- a/util/Config.py +++ b/util/Config.py @@ -3,9 +3,11 @@ import sys import xml import pdb import util.Strings as Strings +import util.Search as Search from logger import main_log, exception_log classArgsMem = {} CONFIG_PATH = 'config/' +DEFAULT_OVERRIDE_MODE = 'Merge' def loadParamRequirementDict(className): if not className in classArgsMem: #WOO CACHING classArgsMem[className] = fileToDict(CONFIG_PATH + className) @@ -16,9 +18,9 @@ def loadConfigFile(fileName): #TODO: error handling etc. if '.params' in fileName: return fileToDict(fileName) if '.xml' in fileName: - config = ElementTree() #use .fromstring, and resolve xincludes + config = ElementTree() config.parse(fileName) - config = ElementTree(resolveConfigInheritance(config.getroot())) + resolveDocumentInheritances(config.getroot()) return config except Exception as inst: main_log.error('Error loading config file ' + fileName)#, inst) TODO: log exception too @@ -30,13 +32,15 @@ def getElement(el): return el elif el.__class__ == ElementTree: return el.getroot() +#XML tree composition. Returns the resulting tree, but happens in-place in the overriding tree. def compositeXMLTrees(parentTree, overridingTree): #TODO: break up into sub-methods, change it to #use .find() #type checking -- convert ElementTrees to their root elements if parentTree == None: return overridingTree if overridingTree == None: - return parentTree + return parentTree #TODO: this will probably cause a bug since it isn't in-place on + #overridingTree parentTree = getElement(parentTree) overridingTree = getElement(overridingTree) parentItems = parentTree.getchildren() @@ -53,7 +57,7 @@ def compositeXMLTrees(parentTree, overridingTree): #TODO: break up into sub-meth main_log.warn('ABUSE! Override of multiple items isn\'t well defined. Don\'t do\ it!') interEl = intersectingElements[0] - mode = 'Replace' + mode = DEFAULT_OVERRIDE_MODE if Strings.OVERRIDE_BEHAVIOR in interEl.attrib: mode = interEl.attrib[Strings.OVERRIDE_BEHAVIOR] if mode != 'Replace' and mode != 'Merge': @@ -91,6 +95,15 @@ def fileToDict(fileName): exception_log.info(fileName + ' is not a well formed python dict. Parsing failed') return eval(fileText) #parses arguments into python objects if possible, otherwise leaves as strings +def pullArgsFromItem(parentNode): + attribArgs = {} + for arg in parentNode.attrib: #automatically pull attributes into the argdict + attribArgs[arg] = parentNode.attrib[arg] + argNode = parentNode.find('Args') + args = generateArgDict(argNode) + for key in attribArgs: + args[key] = attribArgs[key] + return args def generateArgDict(parentNode, recurse=False): args = {} for arg in parentNode.getchildren(): @@ -113,8 +126,13 @@ def generateArgDict(parentNode, recurse=False): if len(args.keys()) == 1 and recurse: return args[args.keys()[0]] return args - -def resolveConfigInheritance(el): +#In place resolution of document inheritances. Doesn't return anything. +def resolveDocumentInheritances(el): + abstractMembers = Search.parental_tree_search(el, '.getchildren()', '.tag==\'InheritsFrom\'') + for subel in abstractMembers: + subel = resolveInheritance(subel) +#In place resolution of inheritence. Doesn't return anything. +def resolveInheritance(el): parentClass = el.find('InheritsFrom') if parentClass != None: parentTree = loadConfigFile(parentClass.text) @@ -122,6 +140,5 @@ def resolveConfigInheritance(el): main_log.warn('Inheritance Failed. ' + parentClass.text + 'does not exist') main_log.error('Inheritance Failed. ' + parentClass.text + 'does not exist') return el - el = compositeXMLTrees(el, parentTree) + el = compositeXMLTrees(parentTree, el) el.remove(parentClass) #get rid of the inheritance flag - return el diff --git a/util/Geo.py b/util/Geo.py index 885c585..a9243de 100644 --- a/util/Geo.py +++ b/util/Geo.py @@ -1,6 +1,7 @@ #Geometry code import math from bisect import * +import random def pointWithinBoundingBox(point, bb): #this could be in 4 lines, but I'm lazy. return sum([(point[i % 2] <= bb[i]) == (i>1) for i in range(4)]) == 4 print pointWithinBoundingBox((118,21), (10,8,298,42)) @@ -13,3 +14,8 @@ def gaussian(x,height,center,width): return a*math.exp(-((x-b)**2)/(2*c**2)) def dist(l1, l2): return math.sqrt(sum([(l1[i]-l2[i])**2 for i in range(len(l1))])) +def randomLoc(boundingBox): #TODO: make less shitty + loc = [] + loc.append(random.randint(0, boundingBox[0])) + loc.append(random.randint(0, boundingBox[1])) + return tuple(loc) diff --git a/util/NetworkOps.py b/util/NetworkOps.py index 2fa531e..6c50c6d 100644 --- a/util/NetworkOps.py +++ b/util/NetworkOps.py @@ -4,7 +4,8 @@ def getConnectedSocket(ip,port): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: sock.connect((ip, port)) + return sock except Exception as inst: main_log.error('Network down. All network based renderers and sensors will not function.', inst) - return sock + print (ip, port) diff --git a/util/PacketComposition.py b/util/PacketComposition.py index 2563c61..73eefff 100644 --- a/util/PacketComposition.py +++ b/util/PacketComposition.py @@ -6,6 +6,7 @@ DEEPMAGIC = 0xc001d00d MAGICHASH = 0x69000420 PORTOUT = 0x0108 UNI = 0 +import pdb kinetDict = {'flags': 0, 'startcode': 0, 'pad':0} def composePixelStripData(pixelStrip): packet = bytearray() @@ -44,7 +45,6 @@ def kinetPortOut(): 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'])) diff --git a/util/Search.py b/util/Search.py index 25882da..f7e4b81 100644 --- a/util/Search.py +++ b/util/Search.py @@ -6,3 +6,17 @@ def find_le(a, x): def find_ge(a, x): 'Find leftmost value greater than x' return bisect_left(a, x) +#returns parents of nodes that meed a given condition +def parental_tree_search(root, childrenstr, conditionstr): + ret = [] + queue = [root] + while queue: + current = queue.pop() + children = eval('current'+childrenstr) + for child in children: + if eval('child'+conditionstr): + ret.append(current) + #we know have a tree, so there are no back-edges etc, so no checking of that kind is + #necessary + queue += children + return ret |