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 From 5b18ae78733cb4ffbc0ca9fd8f13f5ae1f7e37a6 Mon Sep 17 00:00:00 2001 From: rcoh Date: Sat, 12 Feb 2011 20:42:15 -0500 Subject: Woops. --- operationscore/Behavior.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py index 349c4cf..507f750 100644 --- a/operationscore/Behavior.py +++ b/operationscore/Behavior.py @@ -51,13 +51,13 @@ class Behavior(SmootCoreObject): self.addInput(sensorInputs) @staticmethod - def deepCopyPacket(self, datapacket): + 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] + ret.append(d) return ret def getLastOutput(self): @@ -88,7 +88,5 @@ class Behavior(SmootCoreObject): 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) -- cgit v1.2.3 From 84d914b0a015ef6c588253ac7f70382bfa9030e5 Mon Sep 17 00:00:00 2001 From: rcoh Date: Tue, 15 Feb 2011 15:15:43 -0500 Subject: Added documentation to repo in docs folder. --- config/C5Sign.xml | 234 +++++++++++++++++++++++++++++++++++++++++++ docs/Behaviors/Behaviors.pdf | Bin 0 -> 98731 bytes docs/Behaviors/Behaviors.tex | 143 ++++++++++++++++++++++++++ docs/ClassOverview.pdf | Bin 0 -> 132526 bytes docs/designDocs.pdf | Bin 129631 -> 0 bytes docs/designDocs.tex | 192 ----------------------------------- docs/tex/Behaviors.tex | 143 ++++++++++++++++++++++++++ docs/tex/ClassOverview.tex | 186 ++++++++++++++++++++++++++++++++++ ga | 1 + layouts/SpecifiedLayout.py | 21 ++++ renderers/C5Renderer.xml | 10 ++ 11 files changed, 738 insertions(+), 192 deletions(-) create mode 100644 config/C5Sign.xml create mode 100644 docs/Behaviors/Behaviors.pdf create mode 100644 docs/Behaviors/Behaviors.tex create mode 100644 docs/ClassOverview.pdf delete mode 100644 docs/designDocs.pdf delete mode 100644 docs/designDocs.tex create mode 100644 docs/tex/Behaviors.tex create mode 100644 docs/tex/ClassOverview.tex create mode 100644 layouts/SpecifiedLayout.py create mode 100644 renderers/C5Renderer.xml 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 @@ + + + + + simplemap + + + + layouts/C5SignLayout.xml + + + + pixelmappers.SimpleMapper + + simplemap + 20 + + + + pixelmappers.GaussianMapper + + gaussmap + 30 + 0.1 + 10 + 1 + + + + + + renderers/C5Renderer.xml + + + renderers/Pygame.xml + + + + + inputs.PygameInput + + pygameclick + 10 + True + + + + inputs.PygameInput + + pygamekey + 10 + True + + + + inputs.UDPInput + + udp + 3344 + 50 + + + + + inputs/MouseFollower.xml + + + + + behaviors/RandomColor.xml + + + (255,0,0) + (0,0,255) + + + + + behaviors/PixelDecay.xml + + + behaviors/SingleFrame.xml + + + behaviors/PixelDecay.xml + + .01 + + + + behaviors.XYMove + + xymove + 5 + 2 + + + + behaviors.RestrictLocation + + xbounce + {val}*-1 + XStep + {x}<0 or {x}>200 + + + + behaviors.RestrictLocation + + ybounce + {val}*-1 + YStep + {y}<0 or {y}>100 + + + + behaviors.BehaviorChain + + movebounce + + xymove + colorshift + ybounce + xbounce + + + + + behaviors.ModifyParam + + ysin + YStep + Sensor + 4*math.sin({x}/float(40)) + + + + behaviors.DebugBehavior + + debug + 0 + + pygamekey + udp + + + + + behaviors.AllPixels + + square + 20 + + + + behaviors/LoopAndDie.xml + + 80 + + + + behaviors.BehaviorChain + + runcolordecay + + pygameclick + + + colorchange + mover + decay + + {'mover':'movebounce'} + True + gaussmap + + + + behaviors.ResponseMover + + mover + + + + behaviors.RandomWalk + + randmovement + 2 + + + + behaviors/Accelerate.xml + + + behaviors.EchoBehavior + + echo + 0 + False + + + + behaviors.ColorShift + + colorshift + + + + behaviors.BehaviorChain + + mousechaser + + followmouse + + + echo + square + singleframe + + False + + + + behaviors/RunningBehavior.xml + + + diff --git a/docs/Behaviors/Behaviors.pdf b/docs/Behaviors/Behaviors.pdf new file mode 100644 index 0000000..14eafa6 Binary files /dev/null and b/docs/Behaviors/Behaviors.pdf differ diff --git a/docs/Behaviors/Behaviors.tex b/docs/Behaviors/Behaviors.tex new file mode 100644 index 0000000..9021581 --- /dev/null +++ b/docs/Behaviors/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,list). 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} + + behaviors.EchoBehavior + + echo + False + + + \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} + + behaviors.BehaviorChain + + runcolordecay + + pygame + randomLoc + + + colorchange + running + decay + + {'running':'acceleratedie'} + True + gaussmap + + + \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/ClassOverview.pdf b/docs/ClassOverview.pdf new file mode 100644 index 0000000..530dfa6 Binary files /dev/null and b/docs/ClassOverview.pdf differ diff --git a/docs/designDocs.pdf b/docs/designDocs.pdf deleted file mode 100644 index 78eb646..0000000 Binary files a/docs/designDocs.pdf and /dev/null differ diff --git a/docs/designDocs.tex b/docs/designDocs.tex deleted file mode 100644 index 8e62edc..0000000 --- a/docs/designDocs.tex +++ /dev/null @@ -1,192 +0,0 @@ -\documentclass{article} -\usepackage{fullpage} -\begin{document} - \title{150 Smoots Lighting Installation Design Document} - \author{Russell Cohen} - \date{\today} - \maketitle - \newcommand{\classDoc}[5]{ - \subsection{#1} - \begin{itemize} - \item \textbf{Inherits from: } #2 - \item \textbf{Inherited by: } #3 - \item \textbf{Brief Description: } #4 - \item \textbf{Argument Requirements: } #5 - \end{itemize} - } - \section{Intro} - \textbf{NB: These docs give an overview of the classes and methods but - may lag behind the code for certain in-flux functionality. For - up-to-the minute docs, please use pydoc.} \\ - The system, which we will describe henceforth as SmootLight is a - modular system designed with the following goals in mind: - \begin{itemize} - \item The system must abstract away from all components while - remaining useful (\verb=Renderer=, \verb=Input=, \verb=Behavior=) - \item The system must be modular and be easy to write new code for. - \item More goals as I think of them - \end{itemize} - We accomplish this in the following manner: - \begin{itemize} - \item The system is configured by an XML file which specifies its - components. - \item All classes are initialized with a dictionary as an argument - containing anything a class may need. All objects are passed - between members as python dictionaries because their easy - serialization. - \end{itemize} - \section{Overview} - \begin{itemize} - \item - \section{Operations Class Patterns} - \classDoc{SmootCoreObject}{None}{All 2nd level classes (PixelAssembler, Renderer, - Input, Behavior)} - {SmootCoreObject is essentially a super-object - that makes things easy for us. It does the following actions: - \begin{itemize} - \item Defines a constructor that sets argDict - \item Defines a \texttt{\_\_getitem\_\_} , which lets us acces items in - argDict as if the class was a dictionary. - (\texttt{self['itemName']}). It also automatically maps the - initial contents of the argDict to class attributes. - \item Defines validateArgs and validateArgDict which - validate the incoming arguments against a dictionary - containing argument names as keys and an error message to - display if they are missing as a values. This file should - 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 - provides a method \texttt{getLightLocations} which give a list of - all light locations for a given strip. Inheriting classes must - define \texttt{layoutFunc} which returns the next location given the - previous location. (They may simply override - \texttt{getLightLocations} - instead, if they wish, but be careful when doing so). In - heriting classes may defint \texttt{initLayout} which is called at - initialization.}{\begin{itemize} - \item \texttt{lightToLightSpacing}: this is the length of wire - between 2 adjacent LEDs. Common values are 4 or 12. - \item \texttt{numLights}: Number of lights in a strip. - \item \texttt{originLocation}: Location of the first light. - \end{itemize}} - \classDoc{Renderer}{SmootCoreObject}{PygameRenderer, IndoorRenderer}{ - Renderer is a class that serves as an abstract class for renderers - interacting with the system. Inheriting classes must define - render, which is passed a \texttt{lightSystem} object. Inheriting - classes may define initRenderer which is called on initiation. - }{No required arguments} - \classDoc{Input}{SmootCoreObject, threading.Thread}{PygameInput, - TCPInput,UDPInput}{Input is a abstract class which facilitates Inputs. - It does this by providing a method that is polled at a periodic - interval within which the inheriting class can raise an event. - Inheriting classes must define \texttt{sensingLoop} which is - called at the interval specified in the config. Inheriting - classes should call respond with an dictionary as an argument - to raise an event. Classes using (not - inheriting) input must pass a scope into - the argDict which offers a \texttt{processInput} - method. Inputs are marked as Daemon threads, and - are therefore killed when their parent is - killed.}{\begin{itemize} - \item \texttt{InputId}: The string id of a given input. Must be - unique. - \item Optional:\texttt{RefreshInterval}: The interval in - seconds (will soon be changed to milliseconds) between - sucessive calls to the sensingLoop method. TODO: make - timeout. - \end{itemize}} - \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.} -{\begin{itemize} - \item \texttt{Inputs}: A list of input Ids specifying input to the - behavior. In the future, this may also contain behavior ids. -\end{itemize}} -\classDoc{PixelEvent}{SmootCoreObject}{StepResponse}{ - Abstract class defining the behavior of a light after it has been turned on. - Inheriting classes should defint \texttt{lightState} which is passed a - timeDelay in ms as an argument. \texttt{lightState} should return a - color or None if the response is complete.}{\begin{itemize} - \item \texttt{Color}: The color of response. \textit{This is may be - removed in the future} - \end{itemize} - } -\section{The Screen Class and Its Relatives} -\classDoc{Screen}{None}{None} -{The \texttt{Screen} class is a representation of an entire system of pixels, - distributed over space. The Screen class and its relatives process - responses (mapped to pixels via a LayoutEngine), and add PixelEvents - to the individual pixels. The Screen provides an instance that - renderers can call to determine the color of all the individual pixels. It contains a list of PixelStrips, which each - address the individual pixels. \texttt{Screen} offers a - \texttt{respond} method which takes a dictionary containing information - about a response. TODO: detail the contents of this dictionary (maybe - somewhere else). Note: \texttt{Screen} will not process its - responses until \texttt{timeStep} is called which processes all responses that - have been queued since the last time that \texttt{timeStep} was - called. Screen also offers an iterator over \textit{all} lights, - accesible by using an expression like: \texttt{for light in screen:}. For - addressing of specific \texttt{PixelStrips} , \texttt{self.pixelStrips} - is exposed.}{No required parameters} -\classDoc{PixelStrip}{None}{None} -{The \texttt{PixelStrip} class is a representation of a string of Pixels that are - connected in physical space (eg. a strip of lights). A \texttt{PixelStrip} takes a - \texttt{LayoutBuilder} (\textit{Name up for debate, currently known as layout - engine}) as an argument. The \texttt{argDict} of the - \texttt{LayoutBuilder} is - passed becomes the \texttt{argDict} of the \texttt{PixelStrip}. - \texttt{PixelStrip} generally shouldn't be - adressed directly unless you need Strip-Identification for rendering - purposes. You should never, for example, call \texttt{respond} on a - \texttt{PixelStrip} - 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.} - \section{Best Practices} - \subsection{Variable and function naming} - I'm pretty bad about being consistent. However, in all future - code, please adhere to the following: - \begin{itemize} - \item Classes: \texttt{FirstLetterCaps} - \item Functions: \texttt{camelCase} - \item Property Names: \texttt{FirstLetterCaps} - \item Constants: \texttt{ALL\_CAPS\_WITH\_UNDERSCORES} - \end{itemize} - \subsection{Time} - For time, use the \texttt{util.TimeOps.time()} method to return the current - time in ms. - \subsection{Acessing a Component Given an Id} - Use \texttt{util.ComponentRegistry.getComponent(id)}. This provides any - component access to any other components. We may consider a way to - make this read-only. - \subsection{Acessing Pixels} - The ideal method for acessing Pixels in a screen is to use its - iterator. Iterating over the individual PixelStrips is also an - acceptable method. - \subsection{Determining the state of a \texttt{Pixel}} - The best practice for determining the color of a \texttt{Pixel} is to call - \texttt{state}. This ensures - all current active responses running on the Pixel contribute correctly. - \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). - \subsection{Constant Strings (key strings, 'Location', 'Color', etc.)} - Use the util.Strings module. It contains many currently in use - Strings, and ensures consistency. - \end{document} 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,list). 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} + + behaviors.EchoBehavior + + echo + False + + + \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} + + behaviors.BehaviorChain + + runcolordecay + + pygame + randomLoc + + + colorchange + running + decay + + {'running':'acceleratedie'} + True + gaussmap + + + \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/tex/ClassOverview.tex b/docs/tex/ClassOverview.tex new file mode 100644 index 0000000..00d55ec --- /dev/null +++ b/docs/tex/ClassOverview.tex @@ -0,0 +1,186 @@ +\documentclass{article} +\usepackage{fullpage} +\begin{document} + \title{150 Smoots Lighting Installation Design Document} + \author{Russell Cohen} + \date{\today} + \maketitle + \newcommand{\classDoc}[5]{ + \subsection{#1} + \begin{itemize} + \item \textbf{Inherits from: } #2 + \item \textbf{Inherited by: } #3 + \item \textbf{Brief Description: } #4 + \item \textbf{Argument Requirements: } #5 + \end{itemize} + } + \section{Intro} + \textbf{NB: These docs give an overview of the classes and methods but + may lag behind the code for certain in-flux functionality. For + up-to-the minute docs, please use pydoc.} \\ + The system, which we will describe henceforth as SmootLight is a + modular system designed with the following goals in mind: + \begin{itemize} + \item The system must abstract away from all components while + remaining useful (\verb=Renderer=, \verb=Input=, \verb=Behavior=) + \item The system must be modular and be easy to write new code for. + \item More goals as I think of them + \end{itemize} + We accomplish this in the following manner: + \begin{itemize} + \item The system is configured by an XML file which specifies its + components. + \item All classes are initialized with a dictionary as an argument + containing anything a class may need. All objects are passed + between members as python dictionaries because their easy + serialization. + \end{itemize} + \section{Overview} + \begin{itemize} + \item + \section{Operations Class Patterns} + \classDoc{SmootCoreObject}{None}{All 2nd level classes (PixelAssembler, Renderer, + Input, Behavior)} + {SmootCoreObject is essentially a super-object + that makes things easy for us. It does the following actions: + \begin{itemize} + \item Defines a constructor that sets argDict + \item Defines a \texttt{\_\_getitem\_\_} , which lets us acces items in + argDict as if the class was a dictionary. + (\texttt{self['itemName']}). It also automatically maps the + initial contents of the argDict to class attributes. + \item Defines validateArgs and validateArgDict which + validate the incoming arguments against a dictionary + containing argument names as keys and an error message to + display if they are missing as a values. This file should + be named classname.params and look like a python dict + (\texttt{\{'key':value, 'key2':value2\}} ) + \end{itemize} + } + {No required parameters in argDict} + \classDoc{PixelAssembler}{SmootCoreObject}{LineLayout, ZigzagLayout}{ + PixelAssembler is a class that defines the positions of lights. It + provides a method \texttt{getLightLocations} which give a list of + all light locations for a given strip. Inheriting classes must + define \texttt{layoutFunc} which returns the next location given the + previous location. (They may simply override + \texttt{getLightLocations} + instead, if they wish, but be careful when doing so). In + heriting classes may defint \texttt{initLayout} which is called at + initialization.}{\begin{itemize} + \item \texttt{lightToLightSpacing}: this is the length of wire + between 2 adjacent LEDs. Common values are 4 or 12. + \item \texttt{numLights}: Number of lights in a strip. + \item \texttt{originLocation}: Location of the first light. + \end{itemize}} + \classDoc{Renderer}{SmootCoreObject}{PygameRenderer, IndoorRenderer}{ + Renderer is a class that serves as an abstract class for renderers + interacting with the system. Inheriting classes must define + render, which is passed a \texttt{lightSystem} object. Inheriting + classes may define initRenderer which is called on initiation. + }{No required arguments} + \classDoc{Input}{SmootCoreObject, threading.Thread}{PygameInput, + TCPInput,UDPInput}{Input is a abstract class which facilitates Inputs. + It does this by providing a method that is polled at a periodic + interval within which the inheriting class can raise an event. + Inheriting classes must define \texttt{sensingLoop} which is + called at the interval specified in the config. Inheriting + classes should call respond with an dictionary as an argument + to raise an event. Classes using (not + inheriting) input must pass a scope into + the argDict which offers a \texttt{processInput} + method. Inputs are marked as Daemon threads, and + are therefore killed when their parent is + killed.}{\begin{itemize} + \item \texttt{InputId}: The string id of a given input. Must be + unique. + \item Optional:\texttt{RefreshInterval}: The interval in + seconds (will soon be changed to milliseconds) between + sucessive calls to the sensingLoop method. TODO: make + timeout. + \end{itemize}} + \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{processResponse}. Look +at the Behaviors documentation for more details.} +{\begin{itemize} + \item \texttt{Inputs}: A list of input Ids specifying input to the + behavior. +\end{itemize}} +\classDoc{PixelEvent}{SmootCoreObject}{StepResponse}{ + Abstract class defining the behavior of a light after it has been turned on. + Inheriting classes should defint \texttt{lightState} which is passed a + timeDelay in ms as an argument. \texttt{lightState} should return a + color or None if the response is complete.}{\begin{itemize} + \item \texttt{Color}: The color of response. \textit{This is may be + removed in the future} + \end{itemize} + } +\section{The Screen Class and Its Relatives} +\classDoc{Screen}{None}{None} +{The \texttt{Screen} class is a representation of an entire system of pixels, + distributed over space. The Screen class and its relatives process + responses (mapped to pixels via a LayoutEngine), and add PixelEvents + to the individual pixels. The Screen provides an instance that + renderers can call to determine the color of all the individual pixels. It contains a list of PixelStrips, which each + address the individual pixels. \texttt{Screen} offers a + \texttt{respond} method which takes a dictionary containing information + about a response. TODO: detail the contents of this dictionary (maybe + somewhere else). Note: \texttt{Screen} will not process its + responses until \texttt{timeStep} is called which processes all responses that + have been queued since the last time that \texttt{timeStep} was + called. Screen also offers an iterator over \textit{all} lights, + accesible by using an expression like: \texttt{for light in screen:}. For + addressing of specific \texttt{PixelStrips} , \texttt{self.pixelStrips} + is exposed.}{No required parameters} +\classDoc{PixelStrip}{None}{None} +{The \texttt{PixelStrip} class is a representation of a string of Pixels that are + connected in physical space (eg. a strip of lights). A \texttt{PixelStrip} takes a + \texttt{LayoutBuilder} (\textit{Name up for debate, currently known as layout + engine}) as an argument. The \texttt{argDict} of the + \texttt{LayoutBuilder} is + passed becomes the \texttt{argDict} of the \texttt{PixelStrip}. + \texttt{PixelStrip} generally shouldn't be + adressed directly unless you need Strip-Identification for rendering + purposes. You should never, for example, call \texttt{respond} on a + \texttt{PixelStrip} + 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 + code, please adhere to the following: + \begin{itemize} + \item Classes: \texttt{FirstLetterCaps} + \item Functions: \texttt{camelCase} + \item Property Names: \texttt{FirstLetterCaps} + \item Constants: \texttt{ALL\_CAPS\_WITH\_UNDERSCORES} + \end{itemize} + \subsection{Time} + For time, use the \texttt{util.TimeOps.time()} method to return the current + time in ms. + \subsection{Acessing a Component Given an Id} + Use \texttt{util.ComponentRegistry.getComponent(id)}. This provides any + component access to any other components. We may consider a way to + make this read-only. + \subsection{Acessing Pixels} + The ideal method for acessing Pixels in a screen is to use its + iterator. Iterating over the individual PixelStrips is also an + acceptable method. + \subsection{Determining the state of a \texttt{Pixel}} + The best practice for determining the color of a \texttt{Pixel} is to call + \texttt{state}. This ensures + all current active responses running on the Pixel contribute correctly. + \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). + \subsection{Constant Strings (key strings, 'Location', 'Color', etc.)} + Use the util.Strings module. It contains many currently in use + Strings, and ensures consistency. + \end{document} diff --git a/ga b/ga index 7bed4ad..4ec4cc4 100755 --- a/ga +++ b/ga @@ -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 tag in the args dict as follows': + + + (1,1) + (50,50) + + etc. + + 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/renderers/C5Renderer.xml b/renderers/C5Renderer.xml new file mode 100644 index 0000000..d9fc9b0 --- /dev/null +++ b/renderers/C5Renderer.xml @@ -0,0 +1,10 @@ + + renderers.IndoorRenderer + + indoorRenderer + + 10.32.97.17 + {'strip1':1} + + + -- cgit v1.2.3 From f34e1c732036553e4a68534dfc1b24a13ccd33ce Mon Sep 17 00:00:00 2001 From: rcoh Date: Tue, 15 Feb 2011 16:25:37 -0500 Subject: Adding images to the Behaviors Docs --- docs/Behaviors/Behavior.jpg | Bin 0 -> 21577 bytes docs/Behaviors/Behaviors.pdf | Bin 98731 -> 148809 bytes docs/Behaviors/Behaviors.tex | 24 ++++++++++++++++++------ docs/Behaviors/BehaviorwithRecursiveHook.jpg | Bin 0 -> 27186 bytes 4 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 docs/Behaviors/Behavior.jpg create mode 100644 docs/Behaviors/BehaviorwithRecursiveHook.jpg diff --git a/docs/Behaviors/Behavior.jpg b/docs/Behaviors/Behavior.jpg new file mode 100644 index 0000000..c96ee84 Binary files /dev/null and b/docs/Behaviors/Behavior.jpg differ diff --git a/docs/Behaviors/Behaviors.pdf b/docs/Behaviors/Behaviors.pdf index 14eafa6..32c5b75 100644 Binary files a/docs/Behaviors/Behaviors.pdf and b/docs/Behaviors/Behaviors.pdf differ diff --git a/docs/Behaviors/Behaviors.tex b/docs/Behaviors/Behaviors.tex index 9021581..5105588 100644 --- a/docs/Behaviors/Behaviors.tex +++ b/docs/Behaviors/Behaviors.tex @@ -1,5 +1,6 @@ \documentclass{article} \usepackage{fullpage} +\usepackage{graphicx} \begin{document} \title{Behaviors: An Introduction and Exercises} \author{Russell Cohen} @@ -12,6 +13,9 @@ 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 @@ -20,7 +24,7 @@ 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,list). Note that on a + return a value, we return a tuple of \texttt{(list,list)}. 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 @@ -86,7 +90,7 @@ 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: @@ -134,10 +138,18 @@ 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. We also specify which PixelMapper we want to use. - + the screen, by specifying: + \begin{center}\texttt{True} \end{center} + We also specify which PixelMapper we want to use (gaussmap): + \texttt{gaussmap}. \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 new file mode 100644 index 0000000..84e99d6 Binary files /dev/null and b/docs/Behaviors/BehaviorwithRecursiveHook.jpg differ -- cgit v1.2.3