Cookwala / SDK and scenarios / 013

Kofta on the oven, salad on the arm, koshari on the fryer robot

Three recipes, each on the preset built for it: the smart oven bakes the kofta while a person prepares it, the counter arm washes and cuts the salad, and the fryer robot does all of koshari including the deep-fried onions.

Audience: appliance maker · Goal: Help end hunger, Robots for people · Level: beginner

Read first: /docs/CORE/ /docs/QUICKSTART/

Steps

  1. Kofta in the oven: the oven bakes (verified by its oven thermometer); grating, cutting, mixing and forming are the person's. dryRun

  2. Salad on the counter arm: it washes, cuts under its camera, whisks and mixes; chilling and serving are the person's. dryRun

  3. Koshari on the robot with an oil thermometer: twenty steps by the device, the deep fry verified by the oil thermometer. dryRun

  4. The oven alone cannot grate an onion and nobody is there to do it: refused before heat. dryRun

Outcome

Every example recipe now has a device that plans it. An appliance that covers only part of a recipe is still useful when a person is present; alone it is refused with the first missing operation named. Next: scenario 032 looks closer at the oven and its core probe.

Command line

cookwala dryrun examples/kofta-oven.cookwala.json --device examples/capabilities/demo-oven.json --human-present
cookwala dryrun examples/salata-baladi.cookwala.json --device examples/capabilities/demo-arm.json --human-present
cookwala dryrun examples/koshari.cookwala.json --device examples/capabilities/demo-fryer-robot.json --human-present
cookwala dryrun examples/kofta-oven.cookwala.json --device examples/capabilities/demo-oven.json

Recorded output

Status: ok

[1] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.grate", "by": "human", "verifiedBy": "human"}, {"node": "n2", "op": "cw.op.cut", "by": "human", "verifiedBy": "human"}, {"node": "n3", "op": "cw.op.crush", "by": "human", "verifiedBy": "human"}, {"node": "n4", "op": "cw.op.mix", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.form", "by": "human", "verifiedBy": "human"}, {"node": "n6", "op": "cw.op.chill", "by": "human", "verifiedBy": "human"}, {"node": "n7", "op": "cw.op.heat", "by": "device", "verifiedBy": "model", "rung": "model"}, {"node": "n8", "op": "cw.op.bake…
[2] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.wash", "by": "device", "verifiedBy": "time", "rung": "time"}, {"node": "n8", "op": "cw.op.wash", "by": "device", "verifiedBy": "time", "rung": "time"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n9", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.whisk", "by": "device", "verifiedBy": "time", "rung": "…
[3] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.boil", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n2", "op": "cw.op.simmer", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n3", "op": "cw.op.drain", "by": "device", "verifiedBy": "time", "rung": "time"}, {"node": "n4", "op": "cw.op.boil", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n5", "op": "cw.op.boil", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.liquid_temp"}, {"node": "n6", "op": "cw.op.drain", "by": "d…
[4] dryRun: {"state": "refused", "refusal": {"reason": "missing_capability", "node": "n1", "detail": "device cannot perform cw.op.grate and no person is present to do it"}, "plan": []}

Code

Run: python scenarios/out/013-example-recipes-on-fitting-devices/python.py

# Scenario 013: Kofta on the oven, salad on the arm, koshari on the fryer robot
# Three recipes, each on the preset built for it: the smart oven bakes the kofta while a person prepares it, the counter arm washes and cuts the salad, and the fryer robot does all of koshari including the deep-fried onions.
# Run a hub first: python hub/cookwala_hub.py --recipes examples
import json, os
from cookwala.client import CookwalaClient, CookwalaProblem

def load(path):
    with open(path, encoding="utf-8") as f:
        return json.load(f)

def pick(obj, path):
    for part in path.replace("]", "").replace("[", ".").split("."):
        obj = obj[int(part)] if part.isdigit() else (len(obj) if part == "length" else obj.get(part))
    return obj

c = CookwalaClient(os.environ.get("COOKWALA_HUB", "http://localhost:7878"))


# Step 1: Kofta in the oven: the oven bakes (verified by its oven thermometer); grating, cutting, mixing and forming are the person's.
kofta = c.dry_run(recipe_id="kofta-oven", device_id="demo-oven", human_present=True)
assert pick(kofta, "state") == "accepted", pick(kofta, "state")
print("ok", "state", "=", "accepted")
assert pick(kofta, "plan.length") == 10, pick(kofta, "plan.length")
print("ok", "plan.length", "=", 10)
assert pick(kofta, "plan[0].by") == "human", pick(kofta, "plan[0].by")
print("ok", "plan[0].by", "=", "human")
assert pick(kofta, "plan[7].node") == "n8", pick(kofta, "plan[7].node")
print("ok", "plan[7].node", "=", "n8")
assert pick(kofta, "plan[7].op") == "cw.op.bake", pick(kofta, "plan[7].op")
print("ok", "plan[7].op", "=", "cw.op.bake")
assert pick(kofta, "plan[7].by") == "device", pick(kofta, "plan[7].by")
print("ok", "plan[7].by", "=", "device")
assert pick(kofta, "plan[7].rung") == "cw.sense.oven_temp", pick(kofta, "plan[7].rung")
print("ok", "plan[7].rung", "=", "cw.sense.oven_temp")

# Step 2: Salad on the counter arm: it washes, cuts under its camera, whisks and mixes; chilling and serving are the person's.
salad = c.dry_run(recipe_id="salata-baladi", device_id="demo-arm", human_present=True)
assert pick(salad, "state") == "accepted", pick(salad, "state")
print("ok", "state", "=", "accepted")
assert pick(salad, "plan.length") == 9, pick(salad, "plan.length")
print("ok", "plan.length", "=", 9)
assert pick(salad, "plan[2].op") == "cw.op.cut", pick(salad, "plan[2].op")
print("ok", "plan[2].op", "=", "cw.op.cut")
assert pick(salad, "plan[2].by") == "device", pick(salad, "plan[2].by")
print("ok", "plan[2].by", "=", "device")
assert pick(salad, "plan[2].rung") == "cw.sense.vision", pick(salad, "plan[2].rung")
print("ok", "plan[2].rung", "=", "cw.sense.vision")
assert pick(salad, "plan[7].op") == "cw.op.chill", pick(salad, "plan[7].op")
print("ok", "plan[7].op", "=", "cw.op.chill")
assert pick(salad, "plan[7].by") == "human", pick(salad, "plan[7].by")
print("ok", "plan[7].by", "=", "human")

# Step 3: Koshari on the robot with an oil thermometer: twenty steps by the device, the deep fry verified by the oil thermometer.
koshari = c.dry_run(recipe_id="koshari", device_id="demo-fryer-robot", human_present=True)
assert pick(koshari, "state") == "accepted", pick(koshari, "state")
print("ok", "state", "=", "accepted")
assert pick(koshari, "plan.length") == 20, pick(koshari, "plan.length")
print("ok", "plan.length", "=", 20)
assert pick(koshari, "plan[13].op") == "cw.op.deep_fry", pick(koshari, "plan[13].op")
print("ok", "plan[13].op", "=", "cw.op.deep_fry")
assert pick(koshari, "plan[13].by") == "device", pick(koshari, "plan[13].by")
print("ok", "plan[13].by", "=", "device")
assert pick(koshari, "plan[13].rung") == "cw.sense.oil_temp", pick(koshari, "plan[13].rung")
print("ok", "plan[13].rung", "=", "cw.sense.oil_temp")

# Step 4: The oven alone cannot grate an onion and nobody is there to do it: refused before heat.
ovenAlone = c.dry_run(recipe_id="kofta-oven", device_id="demo-oven", human_present=False)
assert pick(ovenAlone, "state") == "refused", pick(ovenAlone, "state")
print("ok", "state", "=", "refused")
assert pick(ovenAlone, "refusal.reason") == "missing_capability", pick(ovenAlone, "refusal.reason")
print("ok", "refusal.reason", "=", "missing_capability")
assert pick(ovenAlone, "refusal.node") == "n1", pick(ovenAlone, "refusal.node")
print("ok", "refusal.node", "=", "n1")

print("scenario complete")

scenarios/013-example-recipes-on-fitting-devices.json

Previous: 012 Next: 014