Let me share with you the new LiveCoding.space 2020 World:  
Collaborative Robots: Rubik's Cube & Lego BOOST.

Here is the outline of the following post:

  • Collaborative Rubik's Cube simulator
  • Croquet VWF and Functional Reactive Streams, why this feature is must have!
  • LiveCoding.space driver for Lego BOOST brick, implementation details

Collaborative Rubik's Cube simulator

Collaborative Rubik's Cube simulator transforms the original Rubik’s Cube demo (by Stewart Smith) that powers the Rubik’s Cube Google Doodle, the Chrome Cube Lab to collaborative one.
Multiple users can explore Rubik's cubes collaboratively and send commands simultaneously. Create a zoo of Rubik's Cubes in virtual worlds.
In LiveCoding.space Rubic's Cube is augmented with virtual robot, that is backed by three physical devices (Lego BOOST brick). So, virtual Rubic's Cube dispatch user commands for rotating faces and executes them sequentially on appropriate devices. Even users, that don't have a connected Lego BOOST brick, could execute commands remotely on other users devices, in the same virtual world.

Croquet VWF and Functional Reactive Streams

In Croquet VWF by default any call of Model's method, like  
vwf.callMethod(nodeId, 'methodName', [parameters]) will go trough VWF Model/View drivers chain for updating the application node tree, executing JS code as soon as possible using proxy 'traps': callingMethod and calledMethod.
That works quit well for synchronous sequential code execution. And also for executing non sequential asynchronous code.
But, that does not suited to work with Promises (async/await), which represents the eventual completion, especially sequences of promises.
So, imagine you have a real physical Rubic's Cube robot, consisting of three independent robot devices (Lego BOOST in our example), every device has for three motors, leds, sensors. And you want to synchronise their work, by sending sequential messages from virtual world, like:
robot1.motorA(90); robot2.motorA(-90); robot1.motorB(-90);. That any next operation waits for a previous operation to complete.
Functional Reactive Streams come as a solution to Croquet VWF and open an unlimited functionality for interconnecting physical world of devices with virtual world in a unique collaborative way, which was not available before!


LiveCoding.space driver for Lego BOOST brick: implementation details

LiveCoding.space 2020 update includes an experimental driver for collaborative synchronous/asynchronous controlling of the external robot device within virtual world.
The implementation of the driver is heavily based on using functional reactive streams. The communication with Lego BOOST brick is done over Web Bluetooth. There is no need for any third party software nor OS driver to start using Lego BOOST inside virtual world. You just need a computer/smart phone with Bluetooth, Web Browser and Lego BOOST brick.

When, Lego BOOST brick is connected, the initialization of reactive functional stream of promises is happened in the driver.

    self.streamScheduler = M.scheduler.newDefaultScheduler();
    const [induce, events] = M.createAdapter();
    self.streamAdapter = {
        induce: induce,
        events: events
    }

    const result = M.concatMap((x) => M.fromPromise(x()), events);
    self.eventsStream = M.tap(res => { tapFunction(res) }, result); 
    M.runEffects(self.eventsStream, self.streamScheduler);

Every method call from the Croquet VWF Model, that is considered to be a Promise, like robotA.setMotorAngle(90), will go through Lego BOOST driver, where it will be added to the stream of promises:

Example of adding LegoBOOST Led action:

let promiseAction = async function () {
    return self.boost.ledAsync(ledColor).then(r => {
        return {
            check: {
                isLegoBoost: isLegoBoost,
                isLegoBoostClone: isLegoBoostClone
            },
            action: "lego",
            nodeID: nodeID,
            methodName: methodName,
            methodParameters: [ledColor],
            res: r
        }
    })
}
self.streamAdapter.induce(promiseAction);

Then, only when Promise from stream will be fulfilled, the side effect will be performed:

    const tapFunction = function (res) {
        if (res && res.action == "lego") {
            if (res.check.isLegoBoost) {
 self.kernel.callMethod(res.nodeID, "sat_" + res.methodName, res.methodParameters)
            }
        }
        console.log(res);
    }

Finally, in LiveCoding.space code editor one could execute the sequence of async code and expect the right order of operations on connected physical devices:

robotA.setLed("red");
robotB.setMotorAngle('A', 180, -100);
robotA.setMotorAngle('B', 90, 100);
robotB.setLed("green");

rubik_app-1