aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Russell Cohen <rcoh@mit.edu>2010-11-29 00:00:26 -0500
committerGravatar Russell Cohen <rcoh@mit.edu>2010-11-29 00:00:26 -0500
commitcf1f2224b3625b01a6aa7db221403849b308b3bc (patch)
tree9ad55077f45efc7a8434688332ee281a28a1cae7
parent9c9babfa7032b443138c4b457aabaf79fad385b3 (diff)
Making recursive behaviors work. Some bugs existed before. Adding running
behavior which makes a signal bounce back and forth.
-rw-r--r--LightInstallation.py10
-rw-r--r--Util.py11
-rw-r--r--behaviors/BehaviorChain.py10
-rw-r--r--behaviors/RunningBehavior.py21
-rw-r--r--config/LightInstallationConfig.xml48
-rw-r--r--docs/designDocs.tex3
-rw-r--r--inputs/TCPInput.py2
-rw-r--r--inputs/UDPInput.py3
-rw-r--r--operationscore/Behavior.py42
-rw-r--r--operationscore/PixelEvent.py7
-rw-r--r--pixelcore/Screen.py30
11 files changed, 138 insertions, 49 deletions
diff --git a/LightInstallation.py b/LightInstallation.py
index 6866271..7d8b7b0 100644
--- a/LightInstallation.py
+++ b/LightInstallation.py
@@ -14,10 +14,11 @@ class LightInstallation:
self.componentDict = {}
self.inputBehaviorRegistry = {} #inputid -> behaviors listening to that
#input
- #give Util a pointer to our componentRegistry so that everyone can use
+ #give Util a pointer to our componentRegistry and screen so that everyone can use
#it
Util.setComponentDict(self.componentDict)
self.screen = Screen()
+ Util.setScreen(self.screen)
config = Util.loadConfigFile(configFileName)
#read configs from xml
rendererConfig = config.find('RendererConfiguration')
@@ -59,9 +60,8 @@ class LightInstallation:
self.renderers = self.initializeComponent(rendererConfig)
def registerComponents(self, components):
for component in components:
- try:
- cid = component['Id']
- except:
+ cid = component['Id']
+ if cid == None:
raise Exception('Components must have Ids!')
self.componentDict[cid] = component
def initializeComponent(self, config):
@@ -114,7 +114,7 @@ class LightInstallation:
if inputId in self.inputBehaviorRegistry: #it could be a behavior
self.inputBehaviorRegistry[inputId].append(behavior['Id'])
def processResponse(self,inputDict, responseDict):
- #pdb.set_trace()
+ print inputDict, responseDict
inputId = inputDict['Id']
boundBehaviorIds = self.inputBehaviorRegistry[inputId]
[self.componentDict[b].addInput(responseDict) for b in boundBehaviorIds]
diff --git a/Util.py b/Util.py
index a398d7c..48ad3c5 100644
--- a/Util.py
+++ b/Util.py
@@ -6,6 +6,7 @@ import socket
import random
from pygame.locals import *
import time as clock
+from pixelevents.StepEvent import *
VERSION = 0x0001
MAGIC = 0x4adc0104
MOREMAGIC = 0xdeadbeef
@@ -16,6 +17,16 @@ UNI = 0
CONFIG_PATH = 'config/'
kinetDict = {'flags': 0, 'startcode': 0, 'pad':0}
componentDict = {}
+#Only for rough estimates. Kindof lazy on specifics.
+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))
+def addLocations(l1,l2):
+ return tuple([l1[i]+l2[i] for i in range(len(l1))])
+def setScreen(screen):
+ globals()["screen"] = screen
+def getScreen():
+ return screen
def setComponentDict(componentDictRef):
globals()["componentDict"] = componentDictRef
def getComponentById(cid):
diff --git a/behaviors/BehaviorChain.py b/behaviors/BehaviorChain.py
index 15f7d74..8bf97bb 100644
--- a/behaviors/BehaviorChain.py
+++ b/behaviors/BehaviorChain.py
@@ -2,9 +2,17 @@ from operationscore.Behavior import *
import Util
import pdb
class BehaviorChain(Behavior):
+ def behaviorInit(self):
+ self.feedback = {} #dictionary to allow feedback of recursives
def processResponse(self, sensorInputs, recursiveInputs):
response = sensorInputs
for behaviorId in self['ChainedBehaviors']:
behavior = Util.getComponentById(behaviorId)
- response = behavior.immediateProcessInput(response)
+ if behaviorId in self.feedback:
+ recurrence = self.feedback[behaviorId]
+ else:
+ recurrence = []
+ (response,recurrence) = behavior.immediateProcessInput(response,\
+ recurrence)
+ self.feedback[behaviorId] = recurrence
return response
diff --git a/behaviors/RunningBehavior.py b/behaviors/RunningBehavior.py
new file mode 100644
index 0000000..255ce69
--- /dev/null
+++ b/behaviors/RunningBehavior.py
@@ -0,0 +1,21 @@
+from operationscore.Behavior import *
+import pdb
+import Util
+class RunningBehavior(Behavior):
+ def processResponse(self, sensorInputs, recursiveInputs):
+ newResponses = sensorInputs
+ ret = []
+ ret += newResponses
+ for recurInput in recursiveInputs:
+ outDict = dict(recurInput)
+ if not 'Dir' in outDict:
+ outDict['Dir'] = 1 #to the right
+ outDict['Location']= Util.addLocations(outDict['Location'],
+ (self['StepSize']*outDict['Dir'],0))
+ if not Util.pointWithinBoundingBox(outDict['Location'], \
+ Util.getScreen().getSize()):
+ outDict['Dir'] *= -1
+ ret.append(outDict)
+ ret += newResponses
+ return (ret, ret)
+
diff --git a/config/LightInstallationConfig.xml b/config/LightInstallationConfig.xml
index 713646e..441b7e4 100644
--- a/config/LightInstallationConfig.xml
+++ b/config/LightInstallationConfig.xml
@@ -14,21 +14,18 @@
<spacing>12</spacing> <!--we can space at any value less the
l2lspacing-->
<numPixels>50</numPixels>
- <originLocation>(10,10)</originLocation>
+ <originLocation>(10,20)</originLocation>
</Args>
</PixelStrip>
<PixelStrip>
- <Class>layouts.ZigzagLayout</Class><!--Name of Layout Class,
- imported dynamically via a eval('import ' + name)-->
- <Args><!--Any args the layout class needs. This go as
- elements of a dictionary that gets passed to the Layout machinery-->
+ <Class>layouts.ZigzagLayout</Class>
+ <Args>
<Id>strip2</Id>
<zigLength>25</zigLength>
<zigAxis>X</zigAxis>
<yDirection>1</yDirection>
<pixelToPixelSpacing>12</pixelToPixelSpacing>
- <spacing>12</spacing> <!--we can space at any value less the
- l2lspacing-->
+ <spacing>12</spacing>
<numPixels>50</numPixels>
<originLocation>(10,30)</originLocation>
</Args>
@@ -46,18 +43,20 @@
<Renderer>
<Class>renderers.PygameRenderer</Class>
<Args>
+ <Id>pygamerender</Id>
<displaySize>(1300,50)</displaySize>
</Args>
</Renderer>
- <Renderer>
+ <!-- <Renderer>
<Class>renderers.IndoorRenderer</Class>
<Args>
+ <Id>indoorRenderer</Id>
<PowerSupply>
<IP>10.1.218.72</IP>
<PortMapping>{'strip1':1, 'strip2':2}</PortMapping>
</PowerSupply>
</Args>
- </Renderer>
+ </Renderer>-->
</RendererConfiguration>
<InputConfiguration>
<InputElement>
@@ -68,14 +67,6 @@
</Args>
</InputElement>
<InputElement>
- <Class>inputs.PygameInput</Class>
- <Args><!--Passed as a dictionary-->
- <Id>followmouse</Id>
- <FollowMouse>True</FollowMouse>
- <RefreshInterval>100</RefreshInterval>
- </Args>
- </InputElement>
- <InputElement>
<Class>inputs.UDPInput</Class>
<Args>
<Id>UDP</Id>
@@ -98,10 +89,11 @@
<Behavior>
<Class>behaviors.ColorChangerBehavior</Class>
<Args>
- <Id>color</Id>
+ <Id>colorchange</Id>
<z-index>0</z-index>
<RenderToScreen>False</RenderToScreen>
<Inputs>
+ <Id>pygame</Id>
</Inputs>
</Args>
</Behavior>
@@ -110,7 +102,7 @@
<Args>
<Id>decay</Id>
<DecayType>Exponential</DecayType>
- <Coefficient>.01</Coefficient>
+ <Coefficient>.005</Coefficient>
<z-index>0</z-index>
<RenderToScreen>False</RenderToScreen>
<Inputs>
@@ -130,16 +122,28 @@
<Behavior>
<Class>behaviors.BehaviorChain</Class>
<Args>
+ <Id>runcolordecay</Id>
<Inputs>
- <Id>followmouse</Id>
+ <Id>pygame</Id>
</Inputs>
<ChainedBehaviors>
- <Id>echo</Id>
- <Id>color</Id>
+ <Id>colorchange</Id>
+ <Id>running</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>10</StepSize>
+ <RenderToScreen>False</RenderToScreen>
+ </Args>
+ </Behavior>
</BehaviorConfiguration>
</LightInstallation>
diff --git a/docs/designDocs.tex b/docs/designDocs.tex
index 327c280..0690d3f 100644
--- a/docs/designDocs.tex
+++ b/docs/designDocs.tex
@@ -179,4 +179,7 @@ argument. This will be passed in via recursive inputs.}
\subsection{Color}
For color, use a tuple of (R,G,B) 0-255 for each. Colors can be
easily manipulated with members of the Util class.
+ \subsection{Locations}
+ Locations are stored (x,y), in whatever unit you light system is
+ in. (Whatever unit you use when you define spacing).
\end{document}
diff --git a/inputs/TCPInput.py b/inputs/TCPInput.py
index 72d8742..01b6a99 100644
--- a/inputs/TCPInput.py
+++ b/inputs/TCPInput.py
@@ -15,8 +15,6 @@ class TCPInput(Input.Input):
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)
diff --git a/inputs/UDPInput.py b/inputs/UDPInput.py
index b0d6c93..5b83792 100644
--- a/inputs/UDPInput.py
+++ b/inputs/UDPInput.py
@@ -7,11 +7,8 @@ class UDPInput(Input):
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)
diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py
index 198c4b2..8db2d88 100644
--- a/operationscore/Behavior.py
+++ b/operationscore/Behavior.py
@@ -2,11 +2,11 @@
#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.
+#define the properties of the light response, (outputs, recursions). They must give a location and
+#color. They may define a PixelEvent to more closely control the outgoing
+#data, however, this is normally handled by routing the event to a behavior
+#specifically designed to do this (like DecayBehavior).
+
import pdb
from operationscore.SmootCoreObject import *
#timeStep is called on every iteration of the LightInstallation
@@ -19,23 +19,43 @@ class Behavior(SmootCoreObject):
self.recursiveResponseQueue = []
self.sensorResponseQueue = []
self.outGoingQueue = []
+ self.behaviorInit()
+ def behaviorInit(self):
+ pass
def processResponse(self, sensorInputs, recursiveInputs):
pass
def addInput(self, sensorInput):
self.sensorResponseQueue.append(sensorInput)
#used for behavior chaining
- def immediateProcessInput(self, sensorInputs):
- return self.processResponse(sensorInputs, [])
+ def immediateProcessInput(self, sensorInputs, recursiveInputs=[]):
+ try:
+ (output,recursions) = self.processResponse(sensorInputs, \
+ recursiveInputs)
+ if type(output) != type([]):
+ output = [output]
+ return (output, recursions)
+ except:
+ return (self.processResponse(sensorInputs, recursiveInputs),[])
def addInputs(self, sensorInputs):
+ print sensorInputs
if type(sensorInputs) == type([]):
[self.addInput(sensorInput) for sensorInput in sensorInputs]
else:
self.addInput(sensorInputs)
- def recursiveReponse(self, args):
- self.responseQueue.append(args)
def timeStep(self):
responses = self.processResponse(self.sensorResponseQueue, \
self.recursiveResponseQueue)
+ if type(responses) == type(tuple()) and len(responses) == 2:
+ (outputs, recursions) = responses
+ else:
+ outputs = responses
+ recursions = []
self.sensorResponseQueue = []
- self.recursiveResponseQueue = []
- return responses
+ self.recursiveResponseQueue = recursions
+ if type(outputs) != type([]):
+ outputs = [outputs]
+ try:
+ return outputs
+ except:
+ pdb.set_trace()
+ return outputs
diff --git a/operationscore/PixelEvent.py b/operationscore/PixelEvent.py
index 8567d93..66b6fdf 100644
--- a/operationscore/PixelEvent.py
+++ b/operationscore/PixelEvent.py
@@ -8,8 +8,11 @@ class PixelEvent(SmootCoreObject):
self.initEvent()
def initEvent(self):
pass
- def scale(c):
- self['Color'] *= c
+ #Returns a new PixelEvent, but with a response scaled by c.
+ def scale(self,c):
+ newDict = dict(self.argDict)
+ newDict['Color'] = Util.multiplyColor(newDict['Color'], c)
+ return self.__class__(newDict)
def state(self,timeDelay):
pass
diff --git a/pixelcore/Screen.py b/pixelcore/Screen.py
index 561dc21..6b5c737 100644
--- a/pixelcore/Screen.py
+++ b/pixelcore/Screen.py
@@ -1,12 +1,17 @@
from pixelcore.Pixel import *
from pixelcore.PixelStrip import *
import itertools
+#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:
def __init__(self):
self.responseQueue = []
self.pixelStrips = []
+ 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
def render(self, surface):
[lS.render(surface) for lS in self.pixelStrips]
def setMapper(self, mapper):
@@ -14,7 +19,8 @@ class Screen:
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])
+ 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.
@@ -26,12 +32,30 @@ class Screen:
#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
+ for light in self:
+ (x,y) = light.location
+
+ minX = min(x, minX)
+ maxX = max(x, maxX)
+
+ minY = min(y, minY)
+ maxY = max(y, maxY)
+ self.size = (minX, minY, maxX, maxY)
+ self.sizeValid = True
+ return (minX, minY, maxX, maxY)
#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]
+ if type(responseInfo) != type(dict()):
+ pdb.set_trace()
pixelWeightList = self.mapper.mapEvent(responseInfo['Location'], self)
Util.addPixelEventIfMissing(responseInfo)
- for (pixel, weight) in pixelWeightList: #TODO: weighting
- pixel.processInput(responseInfo['PixelEvent'], 0) #TODO: z-index
+ for (pixel, weight) in pixelWeightList:
+ pixel.processInput(responseInfo['PixelEvent'].scale(weight), 0) #TODO: z-index