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.
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:
[meta] prefix: com.tangiblebit.type # prefix used for all IDs in this document id: object description: The root of the object hierarchy. [req] # Required properties name(freetext): An informal name of the object. id(string): An ID uniquely identifying the object; to avoid name clashes, you should use "reverse domain name notation" (like Java package) starting with a domain you control. Sample: com.example.coffeemaker if you own the example.com domain. summary(freetext): A short (one line) description of what the object is and what it can be used for. license(string): The license under which the object is made available (must be compatible with CC-SA-BY or a free software/open source license in order to allow inclusion in the TB object database). On the TBit website there is controlled vocabulary of license names that should be used in this field and in the *softwareLicense* field. [opt] # Optional properties homepage(string): The homepage of the object. orderpage(string): A web page where prebuilt copies of the object or kits or components can be ordered. producer(string): The company or organization typically (or initially) producing the object. description(freetext): A longer description of the object. lastModified(timestamp): Timestamp showing when the object metadata or the files or processes connected with the object where last modified. weight(mass): The weight of the object (set to 0 if this is a non-material object). width(length): The total width of the object, when fitting it into a box (0 if this is a non-material object). depth(length): The total depth of the object, when fitting it into a box (0 if this is a non-material object). height(length): The total height of the object, when fitting it into a box (0 if this is a non-material object). softwareLicense(string): The license under which the software related to the object is made available (must be compatible with CC-SA-BY or a free software/open source license in order to allow inclusion in the TB object database). If omitted, the value given in the *license* field is used instead. [list] # Properties occurring once or more category(identifier): List of categories the object belongs to. maintainer(contact): List of people to contact regarding questions about the object. replaces(string): List of object IDs this object is meant to replace/make obsolete. [optlist] # Properties occurring any number of times author(contact): List of the people which created/packaged the object. thumbnail(file): List of small visual depictions of the object (pointers to image files--stored in the *thumbnails* folder). file(file): List of files associated with the object (pointers to files stored in the *files* folder). # Lists of processes associated with the object build: List of processes (IDs) explaining how to build/produce the object. setup: List of processes (IDs) explaining how to setup the object prior to using it (comes between build and usage, and can optionally later be repeated when necessary.) Sample: program channel list on a TV set. use: List of processes (IDs) explaining how to use the object, in general or for some specific purpose. dismantle: List of processes (IDs) explaining how to dismantle/disassemble the object. maintain: List of processes (IDs) explaining how to maintain the object--describes operations that need to be performed regularly or occasionally to keep the object in good order. Sample: change oil in a car. repair: List of processes (IDs) explaining how to repair the object or some parts of it. replace: List of processes (IDs) explaining how to replace same part of the object.
TODO include parsed and beautified version of files instead.
Samples of additional properties that could be defined for a VisualDisplay:
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:
# The basic types that can be used for atomic fields. Types not defined # here are complex types, made up of several atomic or complex types. int: An integer number. External form: as in TDF. float: A floating point number. External form: as in TDF. bool: A Boolean value, either "true" or "false". string: A string. Use this base type for free-form strings that are not meant to be localized. Strings can contain arbitrary Unicode characters. External form: as in TDF. timestamp: A point in time. External form: like the `YAML timestamp <http://yaml.org/type/timestamp.html>`_ type. identifier(string): An identifier, made up of alphanumeric characters and underscores. freetext(string): Text that can be localized (translated in other languages). # Unit types capacitance(float): An electric capacitance, measured by default in farad (F). length(float): A length, measured by default in meter (m). mass(float): A mass, measured by default in kilogram (kg). power(float): A (physical) power, measured by default in watt (W). resistance(float): A electrical resistance or impedance, measured by default in ohm (Ω). voltage(float): A voltage (electrical potential difference), measured by default in volt (V). # CSV types contain several pieces of information wrapped in a single # string, formatted as a CSV fragment: items are comma-separated; if an # item contains commas or newlines, it must be enclosed in quotation marks. # CSV types are defined by giving a comma-separated list of field names in # parentheses; each followed by "=" and the field type; a "?" preceding the # field name marks the field as optional (may be omitted or empty). contact(?givenName=string, ?surname=string, ?emailAddress=string): Provides a minimum of contact information about a person: name and e-mail address.
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).
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:
[meta] prefix: com.tangiblebit.type # prefix used for all IDs in this document id: process description: A process description [req] # Required properties name(freetext): An informal name and short summary of the process. id(string): An ID uniquely identifying the process. Tip: derive from the ID of the corresponding object (e.g. "com.example.coffeemaker.buildit" for a process documenting how to build a "com.example.coffeemaker" object). license(string): The license under which the process is made available (must be compatible with CC-SA-BY or a free software/open source license in order to allow inclusion in the TB process database). On the TBit website there is controlled vocabulary of license names that should be used in this field and in the *softwareLicense* field. [opt] # Optional properties description(freetext): A description of the process. homepage(string): The homepage of the process. lastModified(timestamp): Timestamp showing when the process description or the files connected with the process were last modified. softwareLicense(string): The license under which the software related to the process is made available (must be compatible with CC-SA-BY or a free software/open source license in order to allow inclusion in the TB process database). If omitted, the value given in the *license* field is used instead. [list] # Properties occurring once or more maintainer(contact): List of people to contact regarding questions about the process (typically same as for the corresponding object). [optlist] # Properties occurring any number of times author(contact): List of the people which created/packaged the process (typically same as for the corresponding object). replaces(string): List of process IDs this process is meant to replace/make obsolete. thumbnail(file): A list of small visual depictions of the process (pointers to image files--stored in the *thumbnails* folder). file(file): List of files that are helpful in order to better understand the process, but aren't generally necessary in order to execute it (pointers to files stored in the *files* folder).
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:
Additional properties, not defined by the object category:
The input requirements are often called "bill of materials (BOM)", but they can contain materials and objects.
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:
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.
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.
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:
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:
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.
Keywords (preceding the opening bracket) either specify the relation between the operations in the block:
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:
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) 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.
The following functions are built in:
TODO list: "do", "query", "[object.]run", and "[object.eval]"; return values can be stored using ":=".
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.
TODO Describe how objects and processes are stored in the DB.
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:
-> 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:
-> TODO Or probably just a single file process.tb with up to 7 sections:
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).