diff options
-rw-r--r-- | behaviors/Square.py | 7 | ||||
-rw-r--r-- | config/C5Sign.xml | 234 | ||||
-rw-r--r-- | docs/Behaviors/Behavior.jpg | bin | 0 -> 21577 bytes | |||
-rw-r--r-- | docs/Behaviors/Behaviors.pdf | bin | 0 -> 148809 bytes | |||
-rw-r--r-- | docs/Behaviors/Behaviors.tex | 155 | ||||
-rw-r--r-- | docs/Behaviors/BehaviorwithRecursiveHook.jpg | bin | 0 -> 27186 bytes | |||
-rw-r--r-- | docs/ClassOverview.pdf | bin | 0 -> 132526 bytes | |||
-rw-r--r-- | docs/designDocs.pdf | bin | 129631 -> 0 bytes | |||
-rw-r--r-- | docs/tex/Behaviors.tex | 143 | ||||
-rw-r--r-- | docs/tex/ClassOverview.tex (renamed from docs/designDocs.tex) | 16 | ||||
-rwxr-xr-x | ga | 1 | ||||
-rw-r--r-- | layouts/SpecifiedLayout.py | 21 | ||||
-rw-r--r-- | operationscore/Behavior.py | 29 | ||||
-rw-r--r-- | renderers/C5Renderer.xml | 10 | ||||
-rw-r--r-- | tests/TestBQS.py | 4 | ||||
-rw-r--r-- | util/BehaviorQuerySystem.py | 8 |
16 files changed, 613 insertions, 15 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/config/C5Sign.xml b/config/C5Sign.xml new file mode 100644 index 0000000..6550067 --- /dev/null +++ b/config/C5Sign.xml @@ -0,0 +1,234 @@ +<!---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/C5Renderer.xml</InheritsFrom> + </Renderer> + <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}<0 or {x}>200</LocationRestriction> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.RestrictLocation</Class> + <Args> + <Id>ybounce</Id> + <Action>{val}*-1</Action> + <ParamName>YStep</ParamName> + <LocationRestriction>{y}<0 or {y}>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.AllPixels</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>False</RenderToScreen> + </Args> + </Behavior> + <Behavior Id="running"> + <InheritsFrom>behaviors/RunningBehavior.xml</InheritsFrom> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/docs/Behaviors/Behavior.jpg b/docs/Behaviors/Behavior.jpg Binary files differnew file mode 100644 index 0000000..c96ee84 --- /dev/null +++ b/docs/Behaviors/Behavior.jpg diff --git a/docs/Behaviors/Behaviors.pdf b/docs/Behaviors/Behaviors.pdf Binary files differnew file mode 100644 index 0000000..32c5b75 --- /dev/null +++ b/docs/Behaviors/Behaviors.pdf diff --git a/docs/Behaviors/Behaviors.tex b/docs/Behaviors/Behaviors.tex new file mode 100644 index 0000000..5105588 --- /dev/null +++ b/docs/Behaviors/Behaviors.tex @@ -0,0 +1,155 @@ +\documentclass{article} +\usepackage{fullpage} +\usepackage{graphicx} +\begin{document} + \title{Behaviors: An Introduction and Exercises} + \author{Russell Cohen} + \date{\today} + \maketitle + \section{What is a behavior?} + At its most basic, a behavior is machine with two input terminals and two + output terminals. One of these input terminals is external input. The + other is feedback from the behavior. Similarly, the behavior has two output + terminals. One gets released externally as output, and the other gets fed + back to the behavior. At their core, behaviors have nothing to do with + pixels are light effects -- this is merely how we commonly use them. + \begin{center} + \includegraphics[width=4 in]{Behavior.jpg} + \end{center} + \section{How do I write a behavior?} + At the core of a behavior is its \texttt{ProcessResponse} method which + tells a behavior what to get on input. As you might expect, it has 2 + input ports, and two output ports. The `type' of inputs and outputs can be + anything -- numbers, strings, lists, however, in our system, the inputs + and outputs are all python dictionaries. This allows us to have an + arbitrary number of named parameters. As sample input might look + something like \texttt{{'Location':(20,20), 'Height':10}}. When we + return a value, we return a tuple of \texttt{(list<dict>,list<dict>)}. Note that on a + process response method you will actually be given a \textbf{List of + dictionaries} and you should iterate over them. + \textbf{Important:} You should not directly modify the inputs! Use + \texttt{dict(input)} to create a copy of them! + \section{Exercise 1: addFive} + Our goal: Create a behavior that will add 5 to the 'Value' field of the + input. If no 'Value' field exists, we will set it to five. Below is a + sample \verb processResponse method to do this. Note that process + response is the only part of a behavior that must be written (everything + else happens behind the scenes when you \textbf{inherit} from the + \texttt{Behavior} class. + \begin{verbatim} + def processResponse(self, inputs, recurrences): + output = [] #empty list + for inp in inputs: + inpCopy = dict(inp) + if not ('Value' in inpCopy): + inpCopy['Value'] = 0 + inpCopy['Value'] += 5 + output.append(inpCopy) + return (output, []) #empty list, no recurrences + \end{verbatim} + \section{Exercise 2: A Sum-er} + Create a behavior that outputs the sum of all previous input. Hint: + You will need to use recurrences! + \section{Declaring and Configuring Behaviors} + Once you've written your behavior (or are using an already written + behavior, you will need to tell the light installation to use the + behavior. This is done via XML in the configuration file. When you + run the system, you specify a configuration file eg: + \texttt{python LightInstallation.py config/ConfigFile.xml} + + Behaviors are specified in the \verb BehaviorConfiguration section. + A sample behavior follows: + \begin{verbatim} + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Id>echo</Id> + <RenderToScreen>False</RenderToScreen> + </Args> + </Behavior> + \end{verbatim} + + The ``Class'' attribute specifies the \textbf{Python} class for this + behavior. (The \verb behaviors. prefix tells Python to look in the + behaviors folder). You may recall that all classes SmootLight take a + single python dictionary as an argument -- this is embodied by the + \texttt{Args} tag. A dictionary is created from the XML at runtime + -- this dictionary would be: \texttt{{'Id':'echo', + 'RenderToScreen':False}} + The id we specify is the id that we can reference this behavior by + later. The \verb RenderToScreen attribute specifies whether or not + outputs from this behavior should be directed to the screen (some + behaviors act only as the building blocks for other + behaviors, and are never rendered directly to the screen) + + \section{Behavior Chains} + I have mentioned several times that the system allows for behaviors to + be chained together to create many different effects -- often the + ``motion'' effect and the ``coloring'' effects are two separate + behaviors. The result you see on the screen are these two pieces + connected together. This allows us to build up many different behaviors + from a library of simple pieces. Let's look at how we actually + accomplish this. + + Behavior Chaining is accomplished through the behavior chain class. + Here is an example of a behavior we declare (in XML) via a behavior + chain: + \begin{verbatim} + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>runcolordecay</Id> + <Inputs> + <Id>pygame</Id> + <Id>randomLoc</Id> + </Inputs> + <ChainedBehaviors> + <Id>colorchange</Id> + <Id>running</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'running':'acceleratedie'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> + </Behavior> + \end{verbatim} + + Note the importance of the `Id' field -- that is how we reference all + other components of the system. Let's walk through what is going on + here. We declare this behavior just like any other -- however, for + class, we specify \verb BehaviorChain . The \verb Inputs tag specifies + which inputs will be routed to this behavior. In this case, it is + \verb pygame and \verb randomLoc , two previously declared behaviors. + Inputs from these behaviors will be passed to the behavior chain via + sensorInputs. Next, we have the meet of this chain, the behaviors it is + composed of. This states that first, an input is routed through + \texttt{colorchange}. \verb colorchange adds a color field to the + sensor data. Next, the input is routed to \verb running a behavior + that makes pixels run back and forth. Finally, the input is routed to + \verb decay , a behavior that adds a decay ``PixelEvent'' that makes + individual pixels turn on and then decay. + + The next item we see is \verb RecursiveHooks . This is a special + feature of the \verb BehaviorChain that allows us to augment the + reccurences recursive events have. We specify that we will augment the + recursive behavior of \verb running with another behavior, + \verb acceleratedie which modifies increases the speed of the running + behavior, and stops the behavior after a certain number of iterations. + Note that recursive hooks take data in via their \textbf{external input} + port, and \textbf{not} their recursive port. + \begin{center} + \includegraphics[width=4 in]{BehaviorwithRecursiveHook.jpg} + \end{center} + Finally, we state that this behavior will indeed be rendered directly to + the screen, by specifying: + \begin{center}\texttt{<RenderToScreen>True</RenderToScreen>} \end{center} + We also specify which PixelMapper we want to use (gaussmap): + \texttt{<Mapper>gaussmap</Map>}. \verb gaussmap is the id we assigned to the mapper when + we declared in the \verb PixelMappers section of the xml. + \begin{center} + Phew. This isn't as complicated as it sounds. I promise. + \end{center} + Browse around the behaviors to get an idea of what is possible and what has been done. They + all live in the behaviors folder. Enjoy! + \end{document} diff --git a/docs/Behaviors/BehaviorwithRecursiveHook.jpg b/docs/Behaviors/BehaviorwithRecursiveHook.jpg Binary files differnew file mode 100644 index 0000000..84e99d6 --- /dev/null +++ b/docs/Behaviors/BehaviorwithRecursiveHook.jpg diff --git a/docs/ClassOverview.pdf b/docs/ClassOverview.pdf Binary files differnew file mode 100644 index 0000000..530dfa6 --- /dev/null +++ b/docs/ClassOverview.pdf diff --git a/docs/designDocs.pdf b/docs/designDocs.pdf Binary files differdeleted file mode 100644 index 78eb646..0000000 --- a/docs/designDocs.pdf +++ /dev/null diff --git a/docs/tex/Behaviors.tex b/docs/tex/Behaviors.tex new file mode 100644 index 0000000..9021581 --- /dev/null +++ b/docs/tex/Behaviors.tex @@ -0,0 +1,143 @@ +\documentclass{article} +\usepackage{fullpage} +\begin{document} + \title{Behaviors: An Introduction and Exercises} + \author{Russell Cohen} + \date{\today} + \maketitle + \section{What is a behavior?} + At its most basic, a behavior is machine with two input terminals and two + output terminals. One of these input terminals is external input. The + other is feedback from the behavior. Similarly, the behavior has two output + terminals. One gets released externally as output, and the other gets fed + back to the behavior. At their core, behaviors have nothing to do with + pixels are light effects -- this is merely how we commonly use them. + \section{How do I write a behavior?} + At the core of a behavior is its \texttt{ProcessResponse} method which + tells a behavior what to get on input. As you might expect, it has 2 + input ports, and two output ports. The `type' of inputs and outputs can be + anything -- numbers, strings, lists, however, in our system, the inputs + and outputs are all python dictionaries. This allows us to have an + arbitrary number of named parameters. As sample input might look + something like \texttt{{'Location':(20,20), 'Height':10}}. When we + return a value, we return a tuple of (list<dict>,list<dict>). Note that on a + process response method you will actually be given a \textbf{List of + dictionaries} and you should iterate over them. + \textbf{Important:} You should not directly modify the inputs! Use + \texttt{dict(input)} to create a copy of them! + \section{Exercise 1: addFive} + Our goal: Create a behavior that will add 5 to the 'Value' field of the + input. If no 'Value' field exists, we will set it to five. Below is a + sample \verb processResponse method to do this. Note that process + response is the only part of a behavior that must be written (everything + else happens behind the scenes when you \textbf{inherit} from the + \texttt{Behavior} class. + \begin{verbatim} + def processResponse(self, inputs, recurrences): + output = [] #empty list + for inp in inputs: + inpCopy = dict(inp) + if not ('Value' in inpCopy): + inpCopy['Value'] = 0 + inpCopy['Value'] += 5 + output.append(inpCopy) + return (output, []) #empty list, no recurrences + \end{verbatim} + \section{Exercise 2: A Sum-er} + Create a behavior that outputs the sum of all previous input. Hint: + You will need to use recurrences! + \section{Declaring and Configuring Behaviors} + Once you've written your behavior (or are using an already written + behavior, you will need to tell the light installation to use the + behavior. This is done via XML in the configuration file. When you + run the system, you specify a configuration file eg: + \texttt{python LightInstallation.py config/ConfigFile.xml} + + Behaviors are specified in the \verb BehaviorConfiguration section. + A sample behavior follows: + \begin{verbatim} + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Id>echo</Id> + <RenderToScreen>False</RenderToScreen> + </Args> + </Behavior> + \end{verbatim} + + The ``Class'' attribute specifies the \textbf{Python} class for this + behavior. (The \verb behaviors. prefix tells Python to look in the + behaviors folder). You may recall that all classes SmootLight take a + single python dictionary as an argument -- this is embodied by the + \texttt{Args} tag. A dictionary is created from the XML at runtime + -- this dictionary would be: \texttt{{'Id':'echo', + 'RenderToScreen':False}} + The id we specify is the id that we can reference this behavior by + later. The \verb RenderToScreen attribute specifies whether or not + outputs from this behavior should be directed to the screen (some + behaviors act only as the building blocks for other + behaviors, and are never rendered directly to the screen) + + \section{Behavior Chains} + I have mentioned several times that the system allows for behaviors to + be chained together to create many different effects -- often the + ``motion'' effect and the ``coloring'' effects are two separate + behaviors. The result you see on the screen are these two pieces + connected together. This allows us to build up many different behaviors + from a library of simple pieces. Let's look at how we actually + accomplish this. + + Behavior Chaining is accomplished through the behavior chain class. + Here is an example of a behavior we declare (in XML) via a behavior + chain: + \begin{verbatim} + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>runcolordecay</Id> + <Inputs> + <Id>pygame</Id> + <Id>randomLoc</Id> + </Inputs> + <ChainedBehaviors> + <Id>colorchange</Id> + <Id>running</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'running':'acceleratedie'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> + </Behavior> + \end{verbatim} + + Note the importance of the `Id' field -- that is how we reference all + other components of the system. Let's walk through what is going on + here. We declare this behavior just like any other -- however, for + class, we specify \verb BehaviorChain . The \verb Inputs tag specifies + which inputs will be routed to this behavior. In this case, it is + \verb pygame and \verb randomLoc , two previously declared behaviors. + Inputs from these behaviors will be passed to the behavior chain via + sensorInputs. Next, we have the meet of this chain, the behaviors it is + composed of. This states that first, an input is routed through + \texttt{colorchange}. \verb colorchange adds a color field to the + sensor data. Next, the input is routed to \verb running a behavior + that makes pixels run back and forth. Finally, the input is routed to + \verb decay , a behavior that adds a decay ``PixelEvent'' that makes + individual pixels turn on and then decay. + + The next item we see is \verb RecursiveHooks . This is a special + feature of the \verb BehaviorChain that allows us to augment the + reccurences recursive events have. We specify that we will augment the + recursive behavior of \verb running with another behavior, + \verb acceleratedie which modifies increases the speed of the running + behavior, and stops the behavior after a certain number of iterations. + Note that recursive hooks take data in via their \textbf{external input} + port, and \textbf{not} their recursive port. + + Finally, we state that this behavior will indeed be rendered directly to + the screen. We also specify which PixelMapper we want to use. + + Phew. This isn't as complicated as it sounds. I promise. + + \end{document} diff --git a/docs/designDocs.tex b/docs/tex/ClassOverview.tex index 8e62edc..00d55ec 100644 --- a/docs/designDocs.tex +++ b/docs/tex/ClassOverview.tex @@ -56,8 +56,7 @@ be named classname.params and look like a python dict (\texttt{\{'key':value, 'key2':value2\}} ) \end{itemize} - Note that at this point, the only class using this functionality - is the PixelEvent class.} + } {No required parameters in argDict} \classDoc{PixelAssembler}{SmootCoreObject}{LineLayout, ZigzagLayout}{ PixelAssembler is a class that defines the positions of lights. It @@ -103,17 +102,11 @@ \classDoc{Behavior}{SmootCoreObject}{EchoBehavior, DebugBehavior}{ 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 -spawned during the last time step. Inheriting classes MUST define -\texttt{processBehavior}. \texttt{processBehavior} should return a list of dictionaries which -define the properties of the light response. The must return a location -\texttt{PixelEvent} class. Soon be be deprecated:\textit{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].} -Call \texttt{recursiveResponse} to queue a input on the next iteration with a dictionary -argument. This will be passed in via recursive inputs.} +spawned during the last time step. Inheriting classes MUST define \texttt{processResponse}. Look +at the Behaviors documentation for more details.} {\begin{itemize} \item \texttt{Inputs}: A list of input Ids specifying input to the - behavior. In the future, this may also contain behavior ids. + behavior. \end{itemize}} \classDoc{PixelEvent}{SmootCoreObject}{StepResponse}{ Abstract class defining the behavior of a light after it has been turned on. @@ -155,6 +148,7 @@ argument. This will be passed in via recursive inputs.} directly, unless you really know what you're doing. Well, actually you should never need to do that. never. Don't do it.}{Takes a \texttt{LayoutBuilder} as an argument.} + \end{itemize} \section{Best Practices} \subsection{Variable and function naming} I'm pretty bad about being consistent. However, in all future @@ -1 +1,2 @@ git add *.py */*.py *.xml */*.xml tests/*.py tests/testdata/* +git add docs/*.tex docs/*.pdf docs/*/*.tex docs/*/*.pdf diff --git a/layouts/SpecifiedLayout.py b/layouts/SpecifiedLayout.py new file mode 100644 index 0000000..5a6e963 --- /dev/null +++ b/layouts/SpecifiedLayout.py @@ -0,0 +1,21 @@ +from operationscore.PixelAssembler import * +class SpecifiedLayout(PixelAssembler): + """SpecifiedLayout is a class that allows precise specification of each individual LED. + Configure with a <Locations> tag in the args dict as follows': + <Args> + <Locations> + <Loc>(1,1)</Loc> + <Loc>(50,50)</Loc> + </Locations> + etc. + </Args> + You may put attributes on the Locs so that you don't get confused. + """ + + def layoutInit(self): + self.lightNum = -1 + + def layoutFunc(self, lastLocation): + self.lightNum += 1 + return self['Locations'][self.lightNum] + diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py index 7090a23..507f750 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(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,6 +81,7 @@ 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) diff --git a/renderers/C5Renderer.xml b/renderers/C5Renderer.xml new file mode 100644 index 0000000..d9fc9b0 --- /dev/null +++ b/renderers/C5Renderer.xml @@ -0,0 +1,10 @@ +<Renderer> + <Class>renderers.IndoorRenderer</Class> + <Args> + <Id>indoorRenderer</Id> + <PowerSupply> + <IP>10.32.97.17</IP> + <PortMapping>{'strip1':1}</PortMapping> + </PowerSupply> + </Args> +</Renderer> 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)) |