JavaScript Modules
Engine Compatibility
The method described here does not work in version 1.X of the pico engine.
JavaScript Modules allow you to extend KRL within a pico engine. They are often used to take advantage of platform specific features. For example, extending KRL to natively manage GPIO pins of a raspberry pi hosting a pico engine.
JavaScript Modules can also be used to create pico engine singletons. While every pico should be treated as its own threaded object, restricted resources of an engine should be shared across picos as a singleton. For example, a raspberry pi only has one set of gpio pins. The access to the gpio pins can be encapsulated inside a JavaScript module as a singleton to be used by any pico. Another use could be websocket singleton which any pico could use to send events on.
The following sections describe how JavaScript Modules work:
Defining a JavaScript Module
To create a custom JavaScript Module you must start with a new node project (example).
Currently the pico engine only supports defining these modules in the package.json file of a node project with the pico engine as a dependency. In the future, the engine may use environment variables to define modules, but at the time of writing this, does not.
The package.json needs to lists the pico engine as a dependency (this will cause pico engine to be installed with the `npm install` command) as well as provide configurations for the pico engine.
The pico engine configuration takes a map called "modules
". The "modules
" configuration is JavaScript file paths keyed to module names (see example below). The module name is what will be extended to KRL.
Package.json Example
{
"name": "example-krl-modules",
"private": true,
"dependencies": {
"pico-engine": "^0.13.2"
},
"scripts": {
"start": "pico-engine"
},
"pico-engine": {
"port": 4080,
"modules": {
"myCalcJsModule": "./myJsModule.js"
}
}
}
The actual module is a JavaScript file that exports a map of methods keyed to a map of implementation details (see example below).
The pico engine requires your method implementation details to be a map with the keys type
, args
and fn
.
The type
is a string matching function or action. Which declares whether the method is a function or an action. KRL has restrictions on actions, this is because actions allow the state of the pico to be changed. In the example below the actions do not change the state of a pico or engine, but are an example of how to declare them.
The args
is a list of parameters for the method. The ordering of the args array is the n-tuple of arguments to be passed into your modules method. The engine will create an object of passed in arguments keyed to the parameters defined in args. This object is passed as the first argument to the function defined as fn.
The fn
is the function that implements your method. The function takes a map of arguments keyed to the parameters defined in args as the first parameter, and a callback function as the second parameter. When the pico engine calls this function it will provide the callback. The callback takes two arguments, an error map and any result to be returned, in that order. When passing an error, it can be of any type. (Note for developer team: We need more documentation about error passing.)
JavaScript Module Example
myJsModule.js
module.exports = {
random : { type: "function",
args: ["min","max"],
fn: function(args, callback){
var randomNumber = Math.floor(Math.random()*( args.max - args.min +1) + args.min );
callback(null, randomNumber);
},
},
multiply: { type: "action",
args: ["a", "b"],
fn: function(args, callback){
var result = args.a * args.b;
callback(null, result);
},
},
divide : { type: "action",
args: ["n", "d"],
fn: function(args, callback){
callback(null, ( args.n / args.d ) );
},
},
};
Once you have provided those configuration you can `npm install` and `npm start` the pico engine.
Your pico engine should now allow you to call your defined modules (see example below).
Module Instantiation Example
instantiation
global {
random = function(_min, _max){
myCalcJsModule:random(_min, _max)
}
times = defaction(_left, _right){
myCalcJsModule:multiply(_left,_right)
}
division = defaction(_numerator,_denominator){
myCalcJsModule:divide(_numerator,_denominator)
}
}
Using Npm Inside A JavaScript Module
The pico engine is built on top of node, which makes using npm packages ideal and easy to use to extend KRL. The following example is from the pico rover project. The pico rover is a mecanum wheeled rover that is natively controlled from KRL with a pico engine running on a raspberry pi. A custom JavaScript module is used as a singleton to control motors attached to Adafruit's MotorHAT.
This is a good example of how actions should be defined and used. When the speed of a motor is changed, the physical state is changed. Actions allow the state of a pico to change, the state of a pico represents the physical state of the analog device. When motorHat methods are called they result in a physical state change.
MotorHat Module Instantiation Example
This is an example of how the MotorHat module is used in KRL. This example comes from wheels.krl.
KRL example
MotorHat Library Configuration
MotorHat module takes advantage of the motor-hat npm library. The npm library was installed manually but could be listed as a new dependency of the engine.
This example is taken from motor-hat.js and shows how the npm library was initialized. The motor-hat.js code requires and configures the motor-hat library as directed in documentation.
MotorHat Module Methods
As seen in the previous example, the JavaScript exports methods for the engine to provide in the new module. Below you will see that the motor-hat.js wrapps motor-hat library functions in KRL actions
. This allows developers to take advantage of npm's large librarys to exstend and customize KRL as desired.
Conclusion
JavaScript Modules empower developers to extend KRL to perform low level operations natively in KRL. This has been demonstrated in the example above by exposing hardware control as a module used as a singleton in KRL.
Copyright Picolabs | Licensed under Creative Commons.