---
tags: ose
---
SPDX-FileCopyrightText: Holger Kienle, <hkienle@posteo.de>
SPDX-FileCopyrightText: (add yourself if you contributed)
SPDX-License-Identifier: CC-BY-SA-4.0
:::success
Please contribute by directly editing and/or [commenting](https://hackmd.io/c/tutorials/%2Fs%2Fhow-to-use-comments)!
:::
# bommm: Bill of Materials Master Manager
[ToC]
This working document outlines a textual specification language for (OSE) machines to
- DRAFT-FOR-COMMENT describe their *features* in a hierarchical fashion.
- STARTED describe feature *alternatives*, leading to different possible *configurations*.
- TODO generate an (interactive) graph of the feature tree for documentation.
- TODO generate a textual list / spreadsheet of the (leaf) features (= "virtual BOM")
- STARTED describe *procurable items*.
- TODO map (leaf) features to procurable items.
- TODO describe suppliers
- TODO map procurable items to potential suppliers
- TODO run heuristics to obtain a proposed BOM with procurable items and suppliers (= "physical BOM")
- TODO generate spreadsheet-based BOM (for subsequent manual refinement)
- ...
## Feature tree
:::info
:nerd_face: The underlying representation is a typed, attributed directed-acyclic graph (dag).
:::
The composition of a machine is described in a tree structure that consists of *features* (nodes in a tree). To start specifying a 3D printer:
```
feature printer is ... // top-level node: the machine we want to specify
```
A feature is composed of sub-features that are specified in a list notation. Sub-features are separted with commas (`,`).
```
/*
* 3D printer feature specification
*/
feature printer is
baseplate
, x-axis , y-axis, z-axis // axes are not identical
, ... // more sub-features
```
:::info
:bulb: A feature is "a prominent or distinctive user-visible aspect, quality, or characteristic of a systems" [(SEI report)](https://resources.sei.cmu.edu/library/asset-view.cfm?assetid=11231). In other words, it's everything you want it to be as long as it suits your needs.
:::
:::info
:bulb: Comments are C/C++ style.
:::
The above statement specifices a parent node (`printer`) and the parent node's child nodes (`baseplate`, `x-axis`, etc.). The child nodes are connected to the parent with (implicit, directed) edges.
```graphviz
digraph {
node [fontname=Courier]
"baseplate" -> printer;
"x-axis" -> printer;
"y-axis" -> printer;
"z-axis" -> printer;
"..." -> printer;
}
```
Sub-feature are **mandatory** parts of the (parent-)feature. Because all children are mandatory, you can think of the parent as an "AND-node".
```
feature parent is child-1 , child-2, ... , child-n // parent is an AND node
```
### Arc count attribute
A feature can have multiple identical sub-features. This is expressed with a special `count` attribute, which is attached to the arc:
```
carriage = ... screw-m6 [ count=5 ] ... // the carriage has 5 screws
```
```graphviz
digraph {
node [fontname=Courier]
"screw-m6" -> carriage [label=5];
}
```
:::warning
:nerd_face: The syntax of attributes is inspired by graphviz/dot.
:::
Because this is an attribute with special meaning that is frequently used there is a special syntax for it:
```
carriage = ... 5 * screw-m6 ... // equivalent syntactic sugar
```
:::warning
:nerd_face: [Syntactic sugar](http://en.wikipedia.org/wiki/Syntactic_sugar) is a convenient notation/shortform, which can be also expressed in a more general, but also more verbose, way. (Think of i++ vs. i=i+1.)
:::
All of the following lines have the same meaning:
```
feature parent is ... child ...
feature parent is ... 1* child ...
feature parent is ... child [ count=1 ] ...
```
:::info
:bulb: The count attribute expresses a discrete quantity and has no unit.
:::
:::danger
:fire:
```
feature parent is ... 5 * child [ count=7 ] ... // undefined
```
:::
### Arc attributes
Besides count, an arc can have any number of attribute and they can have different types:
```
feature parent is ... child [ a= 1 ] // number
[ b= true ] // boolean
[ c= "foo" ] // string
[ d= 1/12 ] // fraction: n / m
[ count= 5 ]
...
```
:::info
:bulb: :nerd_face: Arcs, same as nodes, are first-class entities. In other words, both are “[reified](https://en.wikipedia.org/wiki/Reification_(computer_science))”.
:::
:::danger
:fire:
```
feature parent is ... child [ a= 5 ]
[ a= 7 ] // undefined
```
:::
The following lines have the same meaning:
```
... a_part [diy= true]
... a_part [diy] // syntactic sugar
```
Features (tree nodes) can have attributes as well. See below.
### Atomic features (leaf nodes)
Features that have no children are *leaf nodes*. Atomic features can be specified explicitly with:
```
feature screw-m6 is empty // atomic feature, no children
```
This can be seen as explicit documentation, but is also used as a mechanism to attach attributes to features, see below.
### Feature attributes
Similar to arcs, features can have attributes that are used to attach properties to them.
```
feature rod // rod with lenght 35cm, syntactic sugar needed?
[ quantity = 35 ]
[ factor = 1/100 ]
[ unit = "m" ]
is empty
```
### Caveat: features are unique
Features are identified by their names. This means that a feature is unique, and uniquely associated with a tree node. For example, let's specify the screws needed for the printer's Y-axis:
```
feature y-axis is
carriage
, idler
, y-base
feature carriage is 5 * screw-m6-18mm , ...
feature idler is 3 * screw-m6-18mm , ...
feature y-base is 4 * screw-m6-18mm , ...
```
Now, `screw-m6-18mm` is a unique feature, even though it shows up several times in the specification. The resulting tree looks like this:
```graphviz
digraph {
node [fontname=Courier]
carriage -> "y-axis"
idler -> "y-axis"
"y-base" -> "y-axis"
"screw-m6-18mm" -> carriage [label=5]
"screw-m6-18mm" -> idler [label=3]
"screw-m6-18mm" -> "y-base" [label=4]
}
```
:::info
:bulb: (Looking ahead, it's possible to propagate the count attribut towards the root to know that the y-axis needs 12 screws in total.)
:::
### Adding to the feature tree
For modularity and flexibility, it is desirable to add nodes, arcs, and attributes to the tree incrementally.
:::warning
:nerd_face: The feature specification may look declarative, but parts of it will be probably operational (e.g., ordering matters)!
:::
:::danger
:question:
```
feature rod [a=1] is empty
...
feature rod [b=1] is empty // allowed?: add attribute b
...
feature rod is foo // allowed?: rod is no longer atomic, wow!
...
feature rod is bar // allowed?: rod has now two children: foo and bar
```
:::
However, this incremental build-up of the tree makes the specification much harder to understand and maintain!
:::info
:bulb: Avoid incremental build-up of the tree.
:::warning
:nerd_face: In other words, stay declarative, avoid operational behavior!
:::
### Naming conventions
> There are only two hard things in Computer Science: cache invalidation and naming things. -- <https://www.martinfowler.com/bliki/TwoHardThings.html>
The naming of features is important to make the feature tree easy to understand and maintain.
Dashes (`-`) are used for grouping.
How detailed a feature is named may depend on whether the machine has similar parts that need to be distinguished.
```
screw-m6 // there is only one M6 screw (and it has length 18mmm)
```
```
screw-m6 // default: almost all M6 screws have length 18mm
screw-m6-25mm // exception: a few screws have length 25mm
```
```
screw-m6-18mm // all screws are named consistently
screw-m6-25mm // all screws are named consistently
```
Underscores (`_`) in names are used to make features more readable.
```
a4988 // only understood by the initiated
driver // too generic
driver-a4988 // there is only one driver, no need to subgroup
a4988_driver // seem about right for a name
a4988_driver_carrier // maybe even better, maybe too verbose
```
Ampersands (`&`) in names can be used for logical groups. (Again, this is only a naming convention, the ampersand has no semantic meaning.)
```
feature a4988_driver_&_heatsink
is
a4988_driver // every driver carrier...
, heatsink-tiny // ...needs a (tiny) heatsink
```
:::info
:angel: So far, now need for a CamelCase naming convention.
:::
## Unresolved feature tree (nodes with alternatives)
:::warning
:nerd_face: Inspired by [and-or trees](http://en.wikipedia.org/wiki/And-or_tree) and [feature trees](http://en.wikipedia.org/wiki/Feature_model).
:::
Besides AND-nodes, a feature can be expressed as an ALT-node, which lists alternative features seperated with a pipe symbol (`|`).
```
feature parent is child-1 | child-2 | ... | child-n // ALT-node
```
An ALT-node represents a configuration option because exactly *one* child node needs to be picked later on to obtain a valid configuration.
ALT-nodes are useful to distinguish build/design variations.
```
feature power_plug is
plug-us {us} // the "{<cfg>}"-notation is explained later
| plug-euro {europe}
```
```
/* need a better example: this is decided at procurement!? */
feature pulley is // each axis needs a pulley:
pulley-metal // either we buy it...
| pulley-diy // ...or we print it ourselves
```
AND-nodes and ALT-nodes are graphically distinguished with oval and boxed nodes, respectively.
```graphviz
digraph {
node [fontname=Courier]
"AND-node"
"ALT-node" [shape=box]
}
```
### Feature resolution
:construction_worker:
## Modularity
### includes
## Procurable items
A *procurable item*, or simply *procurable* is something that
- is expected that it can be purchased from a supplier
- can be manufactured by ourselves (`diy`)
Typically there will be several procurables that can satisfy a given feature.
For example, for a certain screw, different package sizes may be procured:
```
/* procurables for feature screw-m6-18mm */
procurable screw-m6-18mm-04pcs [count= 4] procures screw-m6-18mm
procurable screw-m6-18mm-50pcs [count=50] procures screw-m6-18mm
...
```
For example, for an Arduino it may be useful to distinguish between different kinds:
```
feature arduino-mega is ...
// let's support the original
procurable arduino-mega-genuine procures arduino-mega
// any cheap clone will do
procurable arduino-mega-clone procures arduino-mega
// this one seem good trade-off regarding price/quality
procurable arduino-mega-joyit procures arduino-mega
```
```
/* specify feature tree */
feature pulley is pulley-metal {default} | pulley-diy
feature pulley-metal is empty
feature pulley-diy [diy] [filament-cm=200] [time-sec=300] is empty
/* specify procurable */
// single piece
procurable pulley-metal-1pcs procures pulley-metal
// package
procurable pulley-metal-5pcs // ? seperate or global name space
[ count=5 ] // package with 5 pieces
procures pulley-metal
```
:::danger
:question: global or separate namespace for features and procurables
```
feature foo is ...
procurable foo procures foo // ? ok or name clash
```
:::
:construction_worker:
## Resources
- First attempt of a specification. Uses differnent terminoloy and syntax. Contains some ideas that are not incorporated yet: https://docs.google.com/document/d/1OKpR-UmcQx-ATKXIcIaU51CezamdNJhNGehdjjXJo84/edit