From c8316a35a1eb292f09db8b7ff36dd33b43dfe8b6 Mon Sep 17 00:00:00 2001 From: rcoh Date: Sat, 12 Feb 2011 20:39:58 -0500 Subject: Added an override of setLastOutput to square.py. Added some pre-built queries into the BQS. Modified the default behavior or setLastOutput to make a deep copy of ouput to prevent accidental interference. --- behaviors/Square.py | 7 +++++++ operationscore/Behavior.py | 31 ++++++++++++++++++++++++++++--- tests/TestBQS.py | 4 +++- util/BehaviorQuerySystem.py | 8 ++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/behaviors/Square.py b/behaviors/Square.py index 9d3223a..5fdaeef 100644 --- a/behaviors/Square.py +++ b/behaviors/Square.py @@ -7,6 +7,7 @@ class Square(Behavior): def processResponse(self, sensorInputs, recursiveInputs): for sensory in sensorInputs:#TODO: consider replicating the dict + sensory['CenterLoc'] = list(sensory['Location']) xLoc = sensory['Location'][0] yLoc = sensory['Location'][1] width = self['Width'] @@ -15,3 +16,9 @@ class Square(Behavior): '{x}<'+str(xLoc+width)+',{x}>'+str(xLoc-width)+\ ',{y}<'+str(yLoc+width)+',{y}>'+str(yLoc-width) return (sensorInputs, recursiveInputs) + + def setLastOutput(self, output): + coutput = Behavior.deepCopyPacket(output) + for data in coutput: + data['Location'] = data['CenterLoc'] + return coutput diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py index 7090a23..349c4cf 100644 --- a/operationscore/Behavior.py +++ b/operationscore/Behavior.py @@ -12,6 +12,7 @@ class Behavior(SmootCoreObject): specifically designed to do this (like AddPixelEvent). timeStep is called on every iteration of the LightInstallation addInput is called on each individual input received, and the inputs queue""" + def init(self): self.validateArgs('Behavior.params') if type(self['Inputs']) != type([]): @@ -21,34 +22,55 @@ class Behavior(SmootCoreObject): self.outGoingQueue = [] self.lastState = None self.behaviorInit() + def behaviorInit(self): pass + def addMapper(fn): def withmap(fn): return self.addMapperToResponse(fn()) return withmap + def processResponse(self, sensorInputs, recursiveInputs): raise Exception('ProcessResponse not defined!') + def addInput(self, sensorInput): self.sensorResponseQueue.append(sensorInput) + #used for behavior chaining + def immediateProcessInput(self, sensorInputs, recursiveInputs=[]): (outputs,recursions) = self.processResponse(sensorInputs, \ recursiveInputs) return self.addMapperToResponse((outputs,recursions)) + def addInputs(self, sensorInputs): if type(sensorInputs) == type([]): [self.addInput(sensorInput) for sensorInput in sensorInputs] else: self.addInput(sensorInputs) - #private + + @staticmethod + def deepCopyPacket(self, datapacket): + """Returns a deep copy of a behavior data packet (a list of dicts) so that modifying the + returned packet will not modify the incoming packet.""" + ret = [] + for d in datapacket: + d = dict(d) + ret.append[d] + return ret + 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 + location. Make sure you keep lastState as a [] of {}. (List of dicts). Additonally, + ensure that you call Behavior.deepCopyPacket on the packet before hand to avoid inadvertent + down-stream modifications. Look at Square.py for an example of this.""" + self.lastState = Behavior.deepCopyPacket(output) + def addMapperToResponse(self, responses): if self['Mapper'] != None: if type(responses) == type(tuple): @@ -59,11 +81,14 @@ class Behavior(SmootCoreObject): r['Mapper'] = self['Mapper'] return responses return responses + def timeStep(self): #TODO: type checking. clean this up (outputs, recursions) = self.processResponse(self.sensorResponseQueue, \ self.recursiveResponseQueue) self.sensorResponseQueue = [] self.recursiveResponseQueue = recursions self.setLastOutput(outputs) + if outputs != []: + pdb.set_trace() main_log.debug(self['Id'] + ' Ouputs ' + str(outputs)) return self.addMapperToResponse(outputs) diff --git a/tests/TestBQS.py b/tests/TestBQS.py index 7316c31..c46b917 100644 --- a/tests/TestBQS.py +++ b/tests/TestBQS.py @@ -13,13 +13,16 @@ class TestBQS(unittest.TestCase): 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 @@ -30,7 +33,6 @@ class TestBQS(unittest.TestCase): 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 diff --git a/util/BehaviorQuerySystem.py b/util/BehaviorQuerySystem.py index 643b95c..688eecb 100644 --- a/util/BehaviorQuerySystem.py +++ b/util/BehaviorQuerySystem.py @@ -7,6 +7,7 @@ def initBQS(): initialized = True def addBehavior(behavior): + """Add a behavior to the behavior registry.""" behaviorList.append(behavior) def query(predicateList): @@ -36,4 +37,11 @@ def query(predicateList): ret.append(output) return ret +def getDistLambda(loc, maxDist): + """Returns a lambda function that checks if for behaviors within maxDist of loc. Can be passed + in as an arg to query.""" + return lambda args:geo.dist(args['Location'], loc) <= maxDist +def getBehaviorsNear(loc, maxdist): + """A premade method to do the common task of finding behavior near a location.""" + return query(getDistLambda(loc, maxDist)) -- cgit v1.2.3