aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar rcoh <rcoh@mit.edu>2011-02-12 16:40:02 -0500
committerGravatar rcoh <rcoh@mit.edu>2011-02-12 16:40:02 -0500
commit5f54bf57128dbca04a9325670a6ad1e03097d5cb (patch)
treef7d5a94975c842cec497dd8b301830fd5a30f7e9
parent1604f05ec0bad79798bbd764cd62bdb20e55f444 (diff)
parentcf1048df72b845ef7fefd5ec5709f7d1b2c4df79 (diff)
Merge branch 'objprops'
Conflicts: Profile.py
-rw-r--r--TestAll.py8
-rw-r--r--behaviors/ColorShift.py16
-rw-r--r--behaviors/ModulateColor.py16
-rw-r--r--config/C5Demo.xml231
-rw-r--r--config/Demo.xml9
-rw-r--r--layouts/C5SignLayout.xml16
-rw-r--r--layouts/ZigzagLayout.py11
-rw-r--r--operationscore/Behavior.py12
-rw-r--r--operationscore/Input.py2
-rw-r--r--operationscore/SmootCoreObject.py13
-rw-r--r--tests/TestBQS.py40
-rw-r--r--tests/TestConfigLoaders.py14
-rw-r--r--tests/__init__.py3
-rw-r--r--util/BehaviorQuerySystem.py39
-rw-r--r--util/ColorOps.py4
-rw-r--r--util/ComponentRegistry.py25
-rw-r--r--util/Config.py15
-rw-r--r--util/Geo.py12
18 files changed, 451 insertions, 35 deletions
diff --git a/TestAll.py b/TestAll.py
index 23b34ea..b24cf5e 100644
--- a/TestAll.py
+++ b/TestAll.py
@@ -1,9 +1,7 @@
import unittest
from unittest import TestLoader
-import tests.TestConfigLoaders
-import tests.TestComponentRegistry
-testSuite = TestLoader().loadTestsFromModule(tests.TestConfigLoaders)
-unittest.TextTestRunner(verbosity=2).run(testSuite)
+import tests
-testSuite = TestLoader().loadTestsFromModule(tests.TestComponentRegistry)
+testSuite = TestLoader().loadTestsFromModule(tests)
unittest.TextTestRunner(verbosity=2).run(testSuite)
+
diff --git a/behaviors/ColorShift.py b/behaviors/ColorShift.py
new file mode 100644
index 0000000..f185b40
--- /dev/null
+++ b/behaviors/ColorShift.py
@@ -0,0 +1,16 @@
+import util.ColorOps as colorOps
+from operationscore.Behavior import *
+import colorsys
+class ColorShift(Behavior):
+ def processResponse(self, sensor, recurs):
+ ret = []
+ for data in sensor:
+ if not 'HSV' in data:
+ data['HSV'] = list(colorsys.rgb_to_hsv(*data['Color']))
+
+ data['HSV'][0] += .01
+ if data['HSV'][0] >= 360:
+ data['HSV'][0] = 0
+ data['Color'] = colorsys.hsv_to_rgb(*data['HSV'])
+ ret.append(data)
+ return (ret,[])
diff --git a/behaviors/ModulateColor.py b/behaviors/ModulateColor.py
new file mode 100644
index 0000000..904526e
--- /dev/null
+++ b/behaviors/ModulateColor.py
@@ -0,0 +1,16 @@
+import util.ColorOps as colorOps
+from operationscore.Behavior import *
+import colorsys
+class ColorShift(Behavior):
+ def processResponse(self, sensor, recurs):
+ ret = []
+ for data in sensor:
+ if not 'HSV' in data:
+ data['HSV'] = colorsys.rgb_to_hsv(data['Color'])
+
+ data['HSV'][0] += .5
+ if data['HSV'][0] >= 360:
+ data['HSV'][0] = 0
+ data['Color'] = colorsys.hsv_to_rgb(data['HSV'])
+ ret.append(data)
+ return (ret,[])
diff --git a/config/C5Demo.xml b/config/C5Demo.xml
new file mode 100644
index 0000000..c0b4402
--- /dev/null
+++ b/config/C5Demo.xml
@@ -0,0 +1,231 @@
+<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.-->
+<LightInstallation>
+ <InstallationConfiguration>
+ <Defaults>
+ <PixelMapper>simplemap</PixelMapper>
+ </Defaults>
+ </InstallationConfiguration>
+ <PixelConfiguration>
+ <InheritsFrom>layouts/C5SignLayout.xml</InheritsFrom>
+ </PixelConfiguration>
+ <PixelMapperConfiguration>
+ <PixelMapper>
+ <Class>pixelmappers.SimpleMapper</Class>
+ <Args>
+ <Id>simplemap</Id>
+ <CutoffDist>20</CutoffDist>
+ </Args>
+ </PixelMapper>
+ <PixelMapper>
+ <Class>pixelmappers.GaussianMapper</Class>
+ <Args>
+ <Id>gaussmap</Id>
+ <CutoffDist>30</CutoffDist>
+ <MinWeight>0.1</MinWeight>
+ <Width>10</Width>
+ <Height>1</Height>
+ </Args>
+ </PixelMapper>
+ </PixelMapperConfiguration>
+ <RendererConfiguration>
+ <Renderer>
+ <InheritsFrom>renderers/Pygame.xml</InheritsFrom>
+ </Renderer>
+ </RendererConfiguration>
+ <InputConfiguration>
+ <InputElement>
+ <Class>inputs.PygameInput</Class>
+ <Args>
+ <Id>pygameclick</Id>
+ <RefreshInterval>10</RefreshInterval>
+ <Clicks>True</Clicks>
+ </Args>
+ </InputElement>
+ <InputElement>
+ <Class>inputs.PygameInput</Class>
+ <Args>
+ <Id>pygamekey</Id>
+ <RefreshInterval>10</RefreshInterval>
+ <Keyboard>True</Keyboard>
+ </Args>
+ </InputElement>
+ <InputElement>
+ <Class>inputs.UDPInput</Class>
+ <Args>
+ <Id>udp</Id>
+ <Port>3344</Port>
+ <RefreshInterval>50</RefreshInterval>
+ </Args>
+ </InputElement>
+ <!--<InputElement>
+ <Class>inputs.TCPInput</Class>
+ <Args>
+ <Id>tcp</Id>
+ <Port>20120</Port>
+ <RefreshInterval>10</RefreshInterval>
+ </Args>
+ </InputElement>-->
+ <InputElement Id="followmouse" RefreshInterval="1000">
+ <InheritsFrom>inputs/MouseFollower.xml</InheritsFrom>
+ </InputElement>
+ </InputConfiguration>
+ <BehaviorConfiguration>
+ <Behavior Id="colorchange">
+ <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom>
+ <Args>
+ <ColorList>
+ <Val>(255,0,0)</Val>
+ <Val>(0,0,255)</Val>
+ </ColorList>
+ </Args>
+ </Behavior>
+ <Behavior Id="decay">
+ <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom>
+ </Behavior>
+ <Behavior Id="singleframe">
+ <InheritsFrom>behaviors/SingleFrame.xml</InheritsFrom>
+ </Behavior>
+ <Behavior Id="slowdecay">
+ <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom>
+ <Args>
+ <Coefficient>.01</Coefficient>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.XYMove</Class>
+ <Args>
+ <Id>xymove</Id>
+ <XStep>5</XStep>
+ <YStep>2</YStep>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.RestrictLocation</Class>
+ <Args>
+ <Id>xbounce</Id>
+ <Action>{val}*-1</Action>
+ <ParamName>XStep</ParamName>
+ <LocationRestriction>{x}&lt;0 or {x}&gt;200</LocationRestriction>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.RestrictLocation</Class>
+ <Args>
+ <Id>ybounce</Id>
+ <Action>{val}*-1</Action>
+ <ParamName>YStep</ParamName>
+ <LocationRestriction>{y}&lt;0 or {y}&gt;100</LocationRestriction>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.BehaviorChain</Class>
+ <Args>
+ <Id>movebounce</Id>
+ <ChainedBehaviors>
+ <Id>xymove</Id>
+ <Id>colorshift</Id>
+ <Id>ybounce</Id>
+ <Id>xbounce</Id>
+ </ChainedBehaviors>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.ModifyParam</Class>
+ <Args>
+ <Id>ysin</Id>
+ <ParamName>YStep</ParamName>
+ <ParamType>Sensor</ParamType>
+ <ParamOp>4*math.sin({x}/float(40))</ParamOp>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.DebugBehavior</Class>
+ <Args>
+ <Id>debug</Id>
+ <z-index>0</z-index>
+ <Inputs>
+ <Id>pygamekey</Id>
+ <Id>udp</Id>
+ </Inputs>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.Square</Class>
+ <Args>
+ <Id>square</Id>
+ <Width>20</Width>
+ </Args>
+ </Behavior>
+ <Behavior Id="recursivedecay">
+ <InheritsFrom>behaviors/LoopAndDie.xml</InheritsFrom>
+ <Args>
+ <InitialResponseCount>80</InitialResponseCount>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.BehaviorChain</Class>
+ <Args>
+ <Id>runcolordecay</Id>
+ <Inputs>
+ <Id>pygameclick</Id>
+ </Inputs>
+ <ChainedBehaviors>
+ <Id>colorchange</Id>
+ <Id>mover</Id>
+ <Id>decay</Id>
+ </ChainedBehaviors>
+ <RecursiveHooks>{'mover':'movebounce'}</RecursiveHooks>
+ <RenderToScreen>True</RenderToScreen>
+ <Mapper>gaussmap</Mapper>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.ResponseMover</Class>
+ <Args>
+ <Id>mover</Id>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.RandomWalk</Class>
+ <Args>
+ <Id>randmovement</Id>
+ <StepSize>2</StepSize>
+ </Args>
+ </Behavior>
+ <Behavior Id="accelerate">
+ <InheritsFrom>behaviors/Accelerate.xml</InheritsFrom>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.EchoBehavior</Class>
+ <Args>
+ <Id>echo</Id>
+ <z-index>0</z-index>
+ <RenderToScreen>False</RenderToScreen>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.ColorShift</Class>
+ <Args>
+ <Id>colorshift</Id>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.BehaviorChain</Class>
+ <Args>
+ <Id>mousechaser</Id>
+ <Inputs>
+ <Id>followmouse</Id>
+ </Inputs>
+ <ChainedBehaviors>
+ <Id>echo</Id>
+ <Id>square</Id>
+ <Id>singleframe</Id>
+ </ChainedBehaviors>
+ <RenderToScreen>True</RenderToScreen>
+ </Args>
+ </Behavior>
+ <Behavior Id="running">
+ <InheritsFrom>behaviors/RunningBehavior.xml</InheritsFrom>
+ </Behavior>
+ </BehaviorConfiguration>
+</LightInstallation>
diff --git a/config/Demo.xml b/config/Demo.xml
index 4e811ba..67e9811 100644
--- a/config/Demo.xml
+++ b/config/Demo.xml
@@ -126,6 +126,7 @@
<Id>movebounce</Id>
<ChainedBehaviors>
<Id>xymove</Id>
+ <Id>colorshift</Id>
<Id>ybounce</Id>
<Id>xbounce</Id>
</ChainedBehaviors>
@@ -206,6 +207,12 @@
</Args>
</Behavior>
<Behavior>
+ <Class>behaviors.ColorShift</Class>
+ <Args>
+ <Id>colorshift</Id>
+ </Args>
+ </Behavior>
+ <Behavior>
<Class>behaviors.BehaviorChain</Class>
<Args>
<Id>mousechaser</Id>
@@ -217,7 +224,7 @@
<Id>square</Id>
<Id>singleframe</Id>
</ChainedBehaviors>
- <RenderToScreen>True</RenderToScreen>
+ <RenderToScreen>False</RenderToScreen>
</Args>
</Behavior>
<Behavior Id="running">
diff --git a/layouts/C5SignLayout.xml b/layouts/C5SignLayout.xml
new file mode 100644
index 0000000..58310d7
--- /dev/null
+++ b/layouts/C5SignLayout.xml
@@ -0,0 +1,16 @@
+ <PixelConfiguration>
+ <PixelStrip>
+ <Class>layouts.ZigzagLayout</Class>
+ <Args>
+ <Id>strip1</Id>
+ <zigLength>10</zigLength>
+ <zigAxis>X</zigAxis>
+ <yDirection>1</yDirection>
+ <pixelToPixelSpacing>20</pixelToPixelSpacing>
+ <spacing>20</spacing> <!--we can space at any value less the
+ l2lspacing-->
+ <numPixels>50</numPixels>
+ <originLocation>(0,0)</originLocation>
+ </Args>
+ </PixelStrip>
+ </PixelConfiguration>
diff --git a/layouts/ZigzagLayout.py b/layouts/ZigzagLayout.py
index 3fc2ea1..665d14e 100644
--- a/layouts/ZigzagLayout.py
+++ b/layouts/ZigzagLayout.py
@@ -1,14 +1,5 @@
from operationscore.PixelAssembler import *
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(PixelAssembler):
"""ZigZagLayout is a 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
@@ -18,7 +9,7 @@ class ZigzagLayout(PixelAssembler):
|
X-X-X-X
|
- X-X-X-X etc.
+ X-X-X-X etc."""
def initLayout(self):
if not 'zigLength' in self.argDict:
raise Exception('zigLength must be defined in argDict')
diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py
index 6424842..7090a23 100644
--- a/operationscore/Behavior.py
+++ b/operationscore/Behavior.py
@@ -1,9 +1,6 @@
-
import pdb
from operationscore.SmootCoreObject import *
from logger import main_log
-#timeStep is called on every iteration of the LightInstallation
-#addInput is called on each individual input received, and the inputs queue
class Behavior(SmootCoreObject):
"""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
@@ -22,6 +19,7 @@ class Behavior(SmootCoreObject):
self.recursiveResponseQueue = []
self.sensorResponseQueue = []
self.outGoingQueue = []
+ self.lastState = None
self.behaviorInit()
def behaviorInit(self):
pass
@@ -44,6 +42,13 @@ class Behavior(SmootCoreObject):
else:
self.addInput(sensorInputs)
#private
+ def getLastOutput(self):
+ return self.lastState
+ def setLastOutput(self, output):
+ """Override to modify state. For example: if you are using a behavior that does uses
+ strings for location specification, you will want to override this to point to a single
+ location. Make sure you keep lastState as a [] of {}. (List of dicts)"""
+ self.lastState = output
def addMapperToResponse(self, responses):
if self['Mapper'] != None:
if type(responses) == type(tuple):
@@ -59,5 +64,6 @@ class Behavior(SmootCoreObject):
self.recursiveResponseQueue)
self.sensorResponseQueue = []
self.recursiveResponseQueue = recursions
+ self.setLastOutput(outputs)
main_log.debug(self['Id'] + ' Ouputs ' + str(outputs))
return self.addMapperToResponse(outputs)
diff --git a/operationscore/Input.py b/operationscore/Input.py
index d3d5644..5a835ec 100644
--- a/operationscore/Input.py
+++ b/operationscore/Input.py
@@ -17,7 +17,7 @@ class Input(ThreadedSmootCoreObject):
self.inputInit()
def respond(self, eventDict):
- #if eventDict != []:
+ eventDict['InputId'] = self['Id']
self.parentScope.lock.acquire()
self.parentScope.processResponse(self.argDict, eventDict)
self.parentScope.lock.release()
diff --git a/operationscore/SmootCoreObject.py b/operationscore/SmootCoreObject.py
index 0d32773..6addb9c 100644
--- a/operationscore/SmootCoreObject.py
+++ b/operationscore/SmootCoreObject.py
@@ -2,6 +2,7 @@ import pdb
import threading
import thread
import util.Config as configGetter
+import types
class SmootCoreObject(object):
"""SmootCoreObject is essentially a super-object class which grants us some niceties. It allows
@@ -34,9 +35,15 @@ class SmootCoreObject(object):
def __setitem__(self,k, item):
self.argDict[k] = item
- def __getitem__(self, item):
- if item in self.argDict:
- return self.argDict[item]
+ def __getitem__(self, key):
+ if key in self.argDict:
+ item = self.argDict[key]
+ if isinstance(item, types.FunctionType):
+ return item(self.argDict) #resolve the lambda function, if it exists
+ #elif isinstance(item, list): #if its a list of items
+ # pass #TODO: consider doing resolution of lambda funcs for items in lists
+ else:
+ return item
else:
return None
def __contains__(self, item):
diff --git a/tests/TestBQS.py b/tests/TestBQS.py
new file mode 100644
index 0000000..7316c31
--- /dev/null
+++ b/tests/TestBQS.py
@@ -0,0 +1,40 @@
+import unittest
+import util.BehaviorQuerySystem as bqs
+from behaviors.ColorChangerBehavior import *
+import util.Geo as geo
+class TestBQS(unittest.TestCase):
+ def setUp(self):
+ bqs.initBQS()
+ b = ColorChangerBehavior({'Id': 'color','ColorList':[(255,0,0)]})
+ c = ColorChangerBehavior({'Id': 'color2', 'ColorList':[(0,0,255)]})
+ bqs.addBehavior(b)
+ bqs.addBehavior(c)
+ b.addInput({'Location':(3,4)})
+ c.addInput({'Location':(5,12)})
+ b.timeStep()
+ c.timeStep()
+ def tearDown(self):
+ bqs.initBQS()
+ def test_simple_query(self):
+ validQuery = lambda args:args['Color']==(255,0,0)
+ invalidQuery = lambda args:args['Color']==(254,0,0)
+ assert bqs.query(validQuery) == [{'Color':(255,0,0), 'Location':(3,4)}]
+ assert bqs.query(invalidQuery) == []
+ def test_dist_query(self):
+ validDist = lambda args:geo.dist(args['Location'], (0,0)) <= 5
+ invalidDist = lambda args:geo.dist(args['Location'], (0,0)) <= 2
+ doubleDist = lambda args:geo.dist(args['Location'], (0,0)) <= 20
+
+ assert bqs.query(validDist) == [{'Color':(255,0,0), 'Location':(3,4)}]
+ assert bqs.query(invalidDist) == []
+ assert bqs.query(doubleDist) == [{'Color':(255,0,0), 'Location':(3,4)}, {'Color':(0,0,255),\
+ 'Location':(5,12)}]
+ def test_complex_queries(self):
+
+ validQuery = lambda args:args['Color']==(255,0,0)
+ doubleDist = lambda args:geo.dist(args['Location'], (0,0)) <= 20
+
+ twoPartPredicate = lambda args:doubleDist(args) and validQuery(args)
+ assert bqs.query(twoPartPredicate) == [{'Color':(255,0,0), 'Location':(3,4)}]
+ assert bqs.query([validQuery, doubleDist]) == [{'Color':(255,0,0), 'Location':(3,4)}]
+
diff --git a/tests/TestConfigLoaders.py b/tests/TestConfigLoaders.py
index c79bbf1..02c8865 100644
--- a/tests/TestConfigLoaders.py
+++ b/tests/TestConfigLoaders.py
@@ -29,6 +29,20 @@ class TestConfigLoaders(unittest.TestCase):
result.write('tests/testdata/inheritanceTESTout.xml')
assert filecmp.cmp('tests/testdata/inheritanceTESTout.xml',\
'tests/testdata/inheritanceTRUTH.xml')
+ #Tests our fancy new XML Eval Function
+ def test_eval(self):
+ assert Config.attemptEval('5') == 5
+ assert Config.attemptEval('{5:10, 12:15}') == {5:10, 12:15}
+ singleLayerLambda = Config.attemptEval('${Val}$*5')
+ assert singleLayerLambda({'Val':2}) == 10
+ doubleLayerLambda = Config.attemptEval("${Val1}$*'${Val2}$'")
+ assert doubleLayerLambda({'Val1':3})({'Val2':7}) == 21
+ conditional = Config.attemptEval("${Val1}$*5=='${Val2}$'")
+ assert conditional({'Val1':5})({'Val2':25}) == True
+ assert conditional({'Val1':5})({'Val2':26}) == False
+
+ onlyDouble = Config.attemptEval("'${Val1}$'*'${Val2}$'")
+ assert onlyDouble({})({'Val1':3, 'Val2':7}) == 21
if __name__ == '__main__':
unittest.main()
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..0365616 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,3 @@
+from TestComponentRegistry import TestComponentRegistry
+from TestConfigLoaders import TestConfigLoaders
+from TestBQS import TestBQS
diff --git a/util/BehaviorQuerySystem.py b/util/BehaviorQuerySystem.py
new file mode 100644
index 0000000..643b95c
--- /dev/null
+++ b/util/BehaviorQuerySystem.py
@@ -0,0 +1,39 @@
+import types
+"""The behavior query system is a module that allows querying behaviors based on lambda-function
+predicates."""
+def initBQS():
+ global behaviorList, initialized
+ behaviorList = []
+ initialized = True
+
+def addBehavior(behavior):
+ behaviorList.append(behavior)
+
+def query(predicateList):
+ """BehaviorQuerySystem.query takes a list of predicates (functions with signature:
+ (behavior,output)), and
+ optionally a behavior to be compared to."""
+ #want to do queries wrt: behavior itself, the behavior packet, the querying behavior
+ if isinstance(predicateList, types.FunctionType):
+ predicateList = [predicateList]
+ elif not isinstance(predicateList, list):
+ raise Exception('Predicate list must be a function or list of functions')
+ global behaviorList, initialized
+ ret = []
+ if not initialized:
+ initBQS()
+
+ for behavior in behaviorList: #Consider every behavior
+ lastOutput = behavior.getLastOutput()
+ for output in lastOutput: #Look at every element it has output
+ validOutput = True
+ for pred in predicateList: #Evaluate every predicate. A predicate is a lambda function that
+ #takes a dict and returns a bool.
+ if not pred(output):
+ validOutput = False
+ break
+ if validOutput:
+ ret.append(output)
+ return ret
+
+
diff --git a/util/ColorOps.py b/util/ColorOps.py
index 4b1162a..796a902 100644
--- a/util/ColorOps.py
+++ b/util/ColorOps.py
@@ -40,3 +40,7 @@ def randomBrightColor():
hue, sat, val = colorsys.hsv_to_rgb(hue, sat, val)
ret = [hue, sat, val]
return floatToIntColor(ret)
+
+class Color(object):
+ def __init__(self, r,g,b):
+ self.rep = [r,g,b]
diff --git a/util/ComponentRegistry.py b/util/ComponentRegistry.py
index 776cd17..be913df 100644
--- a/util/ComponentRegistry.py
+++ b/util/ComponentRegistry.py
@@ -11,16 +11,19 @@ def clearRegistry():
initRegistry()
def removeComponent(cid):
- globals()['Registry'].pop(cid)
+ global Registry
+ Registry.pop(cid)
def getComponent(cid):
- return globals()['Registry'][cid]
+ global Registry
+ return Registry[cid]
#Registry of all components of the light system
#TODO: pick a graceful failure behavior and implement it
def registerComponent(component, cid=None):
+ global Registry
if cid != None:
- globals()['Registry'][cid] = component
+ Registry[cid] = component
else:
try:
cid = component['Id']
@@ -28,22 +31,26 @@ def registerComponent(component, cid=None):
cid = getNewId()
component['Id'] = cid
main_log.debug(cid + 'automatically assigned')
- globals()['Registry'][cid] = component
+ Registry[cid] = component
return cid
def verifyUniqueId(cid):
- return not cid in globals()['Registry']
+ global Registry
+ return not cid in Registry
def removeComponent(cid):
- globals()['Registry'].pop(cid)
+ global Registry
+ Registry.pop(cid)
def getComponent(cid):
- return globals()['Registry'][cid]
+ global Registry
+ return Registry[cid]
def getNewId():
- trialKey = len(globals()['Registry'])
+ global Registry
+ trialKey = len(Registry)
trialId = hashlib.md5(str(trialKey)).hexdigest()
- while trialId in globals()['Registry']:
+ while trialId in Registry:
trialKey += 1
trialId = hashlib.md5(str(trialKey)).hexdigest()
return trialId
diff --git a/util/Config.py b/util/Config.py
index 6fdb0d4..25018a8 100644
--- a/util/Config.py
+++ b/util/Config.py
@@ -1,4 +1,5 @@
from xml.etree.ElementTree import *
+import re
import sys
import xml
import pdb
@@ -112,8 +113,20 @@ def pullArgsFromItem(parentNode):
return args
def attemptEval(val):
+ """Runs an eval if possible, or converts into a lambda expression if indicated. Otherwise,
+ leaves as a string."""
try:
- val = eval(val)
+ if '${' in val and '}$' in val: #TODO: this could be a little cleaner
+ dictVal = re.sub("'\$\{(.+?)\}\$'", "b['\\1']", val) #replace expressions '${blah}$' with b['blah']
+ dictVal = re.sub("\$\{(.+?)\}\$", "a['\\1']", dictVal) #replace all expressions like {blah} with a['blah']
+ if "'${" and "}$'" in val: #nested lambda madness
+ lambdaVal = eval('lambda a: lambda b: ' + dictVal)
+ else:
+ lambdaVal = eval('lambda a:'+dictVal) #TODO: nested lambdas
+ return lambdaVal #convert referential objects to lambda expressions which can be
+ #resolved dynamically.
+ else:
+ val = eval(val)
except (NameError, SyntaxError):
val = str(val)
return val
diff --git a/util/Geo.py b/util/Geo.py
index 0dde80b..211e89d 100644
--- a/util/Geo.py
+++ b/util/Geo.py
@@ -2,8 +2,9 @@
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
+def pointWithinBoundingBox(point, bb):
+ """Returns whether or not a point (x,y) is within a bounding box (xmin, ymin, xmax, ymax)"""
+ return all([(point[i % 2] <= bb[i]) == (i>1) for i in range(4)])
def addLocations(l1,l2):
return tuple([l1[i]+l2[i] for i in range(len(l1))])
@@ -32,3 +33,10 @@ def windtrail(x,y,height,center,width):
b=center
c=width
return a*((math.exp(-((x-b))/(c)))**2)*(math.exp(-((y))/(0.2*c)))**2
+
+class Location(object):
+ def __init__(self,x=0,y=0):
+ self.x = x
+ self.y = y
+ def __add__(self, b):
+ return Location(self.x+b.x, self.y+b.y)