========================== Tangible Bit Object System ========================== The Tangible Bit Object System (TBOS) describes physical objects. It contains including detailed and automatically processable on how they can be build, how they can be used, and what they can be used for. The TBOS takes care of the entire life cycle of an object, including information on maintenance, part replacement, and repair, and disassembly. Each object has *properties* that describe the object. Each object is associated with *processes* that describe how the object can be build, used, maintained, repaired, modified, or dismantled. Object Descriptions ~~~~~~~~~~~~~~~~~~~ The most important property of each object are its *categories* (types). This allows placing all objects in a type hierarchy. Each category extends another category, the most general object category is *Object*; each object is an instance of one or several category (we allow multiple inheritance here). The hierarchy of object categories, as known to Tangible Bit, is stored in ``doc/object_categories.txt`` (TODO should be formalized and moved outside the ``doc`` directory; it should be possible to just refer to categories by ID to remove the dependency on a single authority). The object category also specified which other properties the object has. The properties of the *Object* category, which are shared by all objects, are as follows: .. include:: ../data/types/object.tt :literal: TODO include parsed and beautified version of files instead. Samples of additional properties that could be defined for a VisualDisplay: - resolutionX (int) - resolutionY (int) - displayWidth (length) - displayHeight (length) New object categories can be declared by defining a name and an ID for the category and by specifying the ID of a parent category (*Object* or a category derived from it) and a list of additional properties supported by the new category. Category declarations are given in the Type Declaration Language, cf. ``formats``. The following basic types can be used for additional properties: .. include:: ../data/types/basetypes.tt :literal: All properties that require a unit come with a "natural unit" taken, when possible, from the `International System of Units `_ (meter, kilogram, second, ampere, degree Celsius, candela, joule, watt, volt, hertz, ohm, lumen etc.). Thus units need never be stored in the DB. The obvious exception is money, where a unit (e.g. USD or EUR) must be supplied. Additional properties can also be *complex types*, made up of several atomic or complex types. Examples of complex types are the *file* and *process* properties of the *Object* type. Complex types are declared in the same way as object categories (cf. ``formats``). Process Descriptions ~~~~~~~~~~~~~~~~~~~~ Processes describe the life cycle of an object: how it and be built; how it can be used and what it can be used for; how it can be maintained, repaired, and modified; and how it can be dismantled and discarded or recycled when it has reached the end of its life cycle. Without processes, any object would be pretty useless. Each process description has the following elements: Metadata ++++++++ .. include:: ../data/types/process.tt :literal: Input +++++ Lists materials and components that are required for the process; including the amount or the number of instances required. Materials and objects listed here are used up or transformed during the process. Input requirements are probably listed using the TB query language, which might look like this:: object ? (category ~ SheetMetal & material = Aluminum & width = 1.5 & depth = 0.3 height = 0.003 & copies = 12); material ? (category ~ ABS & amount = 0.5); (12 sheets made from aluminum or steel, size 150x30 cm, height between 3 and 4 mm; and 0.5 kg of ABS plastic.) A name can be declared before the "?", e.g. ``material "MyMaterial" ?``. The name used for referring to the object/material later in scripting, otherwise the category (e.g. "SheetMetal" or "ABS") is used. The `Booleano `_\ -based query syntax is straightforward: * Logical operators: * and: & * or: | * not: ! * Comparison operators: * Equal: = (or maybe ==) * Not equal: != * Less/greater [or equal] than: < > <= >= * "Similarity": ~ . ``A ~ B`` means: * A is a subcategory of B, if A and B are categories * A contains B as a substring (case-insensitive), if A and B are strings (similar to SQL LIKE) * Maybe: abs(A-B) < 0.01, if A and B are numbers (rough equality check) * Undefined (error) otherwise * Items are separated using ``;`` or ``&``, indicating that both are required; or using ``|`` indicating that one of the is required. Additional properties, not defined by the object category: * copies (int): the number of instances required, for discrete items. Default is 1. * amount (mass): the amount (in kilogram) required, for non-discrete items. No default value. Only one of *copies* or *amount* may be specified, but not both. The input requirements are often called "bill of materials (BOM)", but they can contain materials *and* objects. Tools +++++ Lists tools and resources that are used in the process, but that aren't used up. Tools are listed in the same way as inputs, e.g.:: object ? (category ~ WaterHeater & achievableHeat >= 100 & heatableVolume >= 2); (A water heater than can heat 2 liters of water (or more) to 100 degree Celsius (or more), required for ca. 15 minutes.) Additional property: * time (time?): an estimate of the time for which the tool is required--useful e.g. for cost calculations if a tool is shared or rented on a temporal basis. Constraints +++++++++++ Lists conditions that must be true for the process to be performed as expected. Described in the same way as inputs, e.g.:: constraint ? (10 <= EnvironmentTemperature <= 35) (The environmental temperature must be between 10 and 35 °C.) Implementation note: input, tools, and constraints can be stored as "saved queries" in the database. Output ++++++ The output describes the objects that will be created as a result of the process, if any. Output is specified in a simple declarative language:: object:: category: WaterBoiler achievableHeat: {60, 80, 90, 100} # a list heatableVolume: 0..3 # a range power: 700 weight: 2.8 width: 0.28 depth: 0.2 height: 0.29 If an *id* is specified for the object (recommended), this means that all the object properties not explicitly defined can be determined by looking up the object in the database. (A water boiler that can heat up to 3 liters of water to 60, 80, 90, or 100 °C, needs 700 W, weights 2.8 kg etc.) For processes that don't *create* any objects, but only repair, maintain, or use them, no output will be defined. Processes that dismantle objects should list the resulting remains or waste as output. Operations ++++++++++ TODO document new query-like syntax: Most operations will either be "machine x should run file y" or "the user shall do as described in file z". And these operations can be ordered or unordered (order of execution matters, or it doesn't) or there may be alternatives (if the machine understands Polyps, it should execute this file, otherwise we have prepared a STL alternative). So my idea is to have something like the query language we use for defining dependencies in order to define such operations. Idea for a basic building block: * toolname: "filename" -- run file on tool With about 3 special instructions: * run: "filename" -- run the file in this computer (e.g. a Python script) * read: "filename" -- the human user should read the file (e.g. a HTML file) and follow the instructions therein. If two (or more) alternative versions of the file exist, they should be listed as alternatives:: read: (filename.html | filename.txt) Note: recommended documentation format is HTML; alternatives are open file formats such as plain text, PDF, and ODT (plaintext documentation can be generated from HTML calling e.g. ``lynx -dump -nolist`` or ``elinks -no-numbering -no-references``, Markdown or reStructuredText make it possible to go into the other direction). HTML files shouldn't contain any scripts; they should be run through `HTML Tidy `_ to ensure well-formedness. * do: "blah blah blah" -- also instructions for the user, but specified inline as a string instead of a separate file (for short operations) These blocks can then be combined: * A; B -- execute first A, then B (order matters) * A & B -- execute A and B (order doesn't matter) * A | B -- execute A or B (alternatives). Recommended reading: try the first which works (if the tool doesn't understand the file format specified in A, try B instead) TODO largely obsolete: This is the core of the process description: the list of operations to be performed as part of the process. Operations are partially ordered: some must be performed sequentially, others can be performed simultaneously or in arbitrary order. For each operation, there is an *agent* that should realize the operation--either one of the *tools* required for the process (or, in some cases, one of the *inputs* defined?), or a human. Operations will ofter refer to one *files* attached to the process: CNC router should execute the toolpath defined in file X, human should bolt the components together as shown in PDF file Y, etc. *Variables* can be assigned or reassigned by specifying the variable name, followed by ":=" and an expressions (as in Polyps):: varname: 1 + 2; Operations are arranged in *blocks* (enclosed in curly brackets) that can be named and/or annotated with a keyword. Keyword blocks ^^^^^^^^^^^^^^ Keywords (preceding the opening bracket) either specify the relation between the operations in the block: sequential Operations must be executed in the defined order--this is the default. parallel Operations can be executed in parallel or in arbitrary order. alternative It's sufficient to execute on of the operations. Samples:: parallel { # execute these operations in any order doThis(); doThat(); doSomethingElse(); } sequential { # You can use this inside a parallel block. Otherwise you # won't need it, as all other blocks are sequential by # default. doThis(); doThat(); doSomethingElse(); } alternative { # select one of those processes doThis(); doThat(); doSomethingElse(); } Or they specify that the block should be executed zero, once, or possibly several times depending on the value of an expression; or that it should be executed for all the items in a list: if *expression* Executes the block if *expression* evaluates to true; optionally followed by an *elif* or *else* block. elif *expression* Executes the block if *expression* evaluates to true and if none of the preceding *if* and *elif* blocks was executed; only allowed immediately after an *if* and *elif* block. else Executes the block if none of the preceding *if* and *elif* blocks was executed; only allowed immediately after an *if* and *elif* block. while *expression* The block is executed any number of times, as long as *expression* evaluates to true. for *varname* in *list* Executes the block for each of the members in the *list*, setting *varname* to the value of the currently selected list member. for *varname* in *range* Executes the block for each integer value within the specified range, setting *varname* to the current value; both the lower and the upper limits of the range can be either inclusive or exclusive; both limits of the range must be integer values. Specifying a number *n* instead of a range is identical to specifying the range *[0..n[* (the loop is executed *n* times, indexing starts from 0). Samples:: if *condition* { doThis(); doThat(); doSomethingElse(); } elif *another-condition* { doAnotherThing() } else { doSomethingStrange() } while *condition* { doThis(); doThat(); doSomethingElse(); } # The argument of a for loop is a list... for file in [fileA, fileB, fileC] { # do this for each of the 3 files doThis(); doThat(); } # ...or a range... for i in [1..10] { # repeat 10 times (increasing the value of i) doThis(); doThat(); } # ...or a number... for i in 10 { # repeat 10 times, but this time counting from 0 doThis(); doThat(); } Functions (Named Blocks) ^^^^^^^^^^^^^^^^^^^^^^^^ Functions (named blocks) are defined by prepending a function name followed by a (possibly empty) parameter list before the actual block, separated by ":=". Parameters are comma-separated and enclosed in parentheses. Functions must be declared at the highest level, they can't be nested within another block. Function blocks are initially skipped, they must be explicitly invoked to be executed. Functions can only be invoked after they have been defined. Samples for defining and invoking functions:: # Defining a function with one parameter sharpenTheSaw(sharpness) := { ... } # Defining a parameterless function: prepareTools() := parallel { # function name and keyword can be combined # (it does not matter in which order the tools are prepared) sharpenTheSaw(10); fillWaterInTheHeater(); preheatTheFabber(); } # Defining a function with multiple parameters buildLegs(numOfLegs, kindOfWood, diameter) := { ... } Each process must have exactly one function named *main*; the process is run be executing this function. If the process accepts parameters (cf. below), the *main* function must accept each of these parameters as parameter (order doesn't matter, additional parameters are not allowed); if the process doesn't support parametrization, *main* must not accept any parameters. Built-in Functions ^^^^^^^^^^^^^^^^^^ The following functions are built in: TODO list: "do", "query", "[object.]run", and "[object.eval]"; return values can be stored using ":=". Parameters ++++++++++ TODO can be specified as ranges and lists, but how to distinguish from normal ranges and lists? Use "?=" instead of ":=" ? It should be possible for a process to accept *parameters* that influence the process--for example, if you built a chair, you can configure the exact length of the legs, specify whether it should have a cushion, select the color etc. For each parameter, the supported range or enumeration of values should be predefined; the user then chooses among the available options. The choice of parameter values can influence the other elements of the process--exact input requirements and output specification will depend on them; some of the operations will change to reflect to them; possibly, the tools required will change too. When storing options in the DB, the whole range or enum should be stored in order to be searchable (e.g. a chair that can be made in any color with legs between 35 cm and 80 cm should match a search for a red chair with 50 cm legs.) That's more difficult for input/tool requirements, however (we can calculate and store how much material a 35 cm or a 80 cm chair would need, but we can't precalculate and store such information for *all* possible values). A possible, though imperfect solution would be to store such information for the *minimum* and *maximum* values of numeric parameters, and for a *default* value for enum parameters; and to calculate the exact values on demand. Example of declaring parameters:: # leg length is between 35 cm and 80 cm leg-length: 0.35..0.80; # six different colors are available color: {red, green, blue, yellow, black, white}; Example of selecting parameters--syntax is identical to the syntax used for defining inputs:: # we want a blue chair with 50 cm legs leg-length = 0.5 & color = "blue"; TODO calculate output parameters to input parameters and vice versa: *parametrize,* optionally *invParametrize*. Object Database ~~~~~~~~~~~~~~~ TODO Describe how objects and processes are stored in the DB. Tangible Bit Object Description ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The *Tangible Bit Object* file format (extension: *.tbob*) allows extracting objects with their relevant processes from the database and sharing them computers and users. A TBOB file can contain one or many objects with their relevant processes. A TBOB package is a ZIP file (like formats such as ODT and EPUB). First-level directories: the file contains one directory for objects and one for each type of processes it contains:: objects build setup use maintain repair replace (Empty directories can be omitted.) Second-level directories: Each object and each process is stored in a subdirectory of the corresponding folder, using the object/process ID as directory name (e.g. ``objects/com.example.coffeemaker``). Each object is represented by three simple text files, written in the Tangible Bit Declaration Language: * properties.tdl: contains the object properties * processes.tdl: index of processes associated with the object * files.tdl: index of files associated with the object -> TODO Or probably just a single file ``object.tb`` defining the object properties (without any sections). Each process is represented by five simple text files: * properties.tdl: contains the process metadata * dependencies.tql: lists the input, tools, and constraints required (written in Tangible Bit Query Language) * result.tdl: defines the output and the allowed parameter settings * operations.top: defines the list of operations to be performed (written in Tangible Bit Operations Language) * files.tdl: index of files associated with the process -> TODO Or probably just a single file ``process.tb`` with up to 7 sections: * [properties]: defines the process properties * [constraints]: the constraints that must be fulfilled in order to run the process (optional) * [tools]: the required tools (optional) * [input]: the required input (optional) * [output]: the output of the process (optional) * [operations]: list of operations (instructions to be followed by a human or a machine) (optional) * [files]: other files associated with the process (optional) TODO explain "dirball" concept for instructions (e.g. HTML ball). If an object or process contains files, they are stored in a third-level folder named ``files``. TODO mimetype file?; internationalization/translated strings? Sample directory structure:: objects/ com.example.coffeemaker/ properties.tdl processes.tdl files.tdl files/ thumbnail.gif frontview.jpg build/ com.example.coffeemaker.buildit/ properties.tdl dependencies.tql result.tdl operations.top files.tdl files/ blueprint.iges TODO Ensure that it's OK to package GPL'ed software with non-GPL'ed content in a single ZIP file (should be). To Do ~~~~~ * Decide on syntax to use in *formattedtext* (Markdown or WikiCreole?). * Write sample object package (Drawdio?) and tutorial/overview document on documenting and packing objects. * Get in dialog with people creating and using free designs about what they need and whether our approach suits them. * Document format of declarations (DecL language) and write parser; define literal form / parse expression of the *basetypes*. * Write database generator that reads the type declarations and creates or updates the database scheme accordingly. * Define and publish controlled vocabulary of recommended license names and of the roles of files. * See TODOs above.