温室ひとつにセンサーが何本も付く。温度、湿度、CO2。そこに換気窓やバルブといった動くものがさらに付く。
これは最初に設置するときは問題にならない。三本なら三本をコードに書けばいい。問題は三年後に来る。 温度計が壊れて別の製品に替える。その製品は華氏で測る。CO2 センサーをもう一本足す。バルブを追加する。
そのたびに制御コードを直さねばならないなら、その温室は設置した人なしには直せない代物になる。
ノードが自分を語る
このサンプルでは各ノードが六つの欄で自分を宣言する。
printf("%s{\"id\":\"%s\",\"role\":\"%s\",\"kind\":\"%s\","
"\"model\":\"%s\",\"unit\":\"%s\",\"value\":%.1f}",
i ? "," : "", n->id, n->role, n->kind, n->model,
n->unit, n->value);
id · role · kind · model · unit · value。
unit が欄としてあることが核心だ。 華氏で測る温度計は華氏だと言う。人のために先回りして摂氏に直してやったりしない。ハードウェアは自分の知っていることだけを正直に言い、合わせる仕事は上でやる。
合わせる場所はひとつだ
/// The value moved into the unit the rules use.
///
/// This is the only place in this sample that knows about units, and it
/// decides from the unit a node declared, not from a model number. A sensor
/// nobody has ever heard of still states its unit, so it lands here exactly.
double get canonicalValue {
switch (unit) {
case 'F':
return (value - 32) * 5 / 9;
default:
return value;
}
}
型番で分岐したなら、この場所がそのまま 一覧になる。新しい製品を買うたびに行が増え、一覧に無い製品が入ってくると黙って誤った値を使う。
unit を見れば、初めて見る製品でも勝手に合う。
規則は部品番号を知らない
栽培規則ひとつが七行だ。
Rule(
name: 'vent above 26C',
whenKind: 'temperature',
above: 26.0,
thenKind: 'vent',
setTo: 80.0,
elseSetTo: 0.0,
),
どのセンサーかが書かれていない。 どのリレーかも書かれていない。読む値の 種類 と、動かすものの 種類 だけがある。
TH-100 でも FX-200 でも、kind が temperature ならこの規則が見る。
実際に差し替えてみた
設置 A — 摂氏の温度計、ノード三つ。
A: discovered 3 nodes — t1:temperature:TH-100(C), h1:humidity:HM-20(pct), v1:vent:VT-9(pct)
A: vent above 26C: 26.1 -> v1=80.0


ここで温度計を 華氏のモデルに替え、CO2 センサーとバルブを 増設する。
=== pass B: probe swapped for a Fahrenheit model, two nodes added ===
server code: unchanged rules: unchanged rebuild: none
B: discovered 5 nodes — t1:temperature:FX-200(F), h1:humidity:HM-20(pct),
c1:co2:CO-5(ppm), v1:vent:VT-9(pct), w1:valve:WV-3(pct)
B: vent above 26C: 26.1 -> v1=80.0 · water below 60% humidity: 62.8 -> w1=0.0


サーバーコード変更なし。規則変更なし。再ビルドなし。
そして vent above 26C が二つの設置で 同じく 26.1 で発動した。 華氏のセンサーが送った値が一箇所で摂氏に移されたからだ。その一箇所が無ければ華氏 79 度が 26 度の閾値をそのまま素通りして、換気窓は開いたままだっただろう。