node.js classcad instance manager


License
ISC
Install
npm install awv3-node@9.0.3

Documentation

ClassCAD instance manager based on Node.js

This is a Node.js implementation using Socket.IO as the communications protocol.

Install

npm install awv3-node

Instance server

The instance manager represents a single endpoint where users connect in order to work with ClassCAD sessions. It may ship ClassCAD binaries and boot them up when starting, but it can also rely on seperate 'farms', servers that host ClassCAD processes.

const Server = require('awv3-node');

// Start server using custom ClassCAD binaries
let server = new Server({

    // optional: zip that contains ClassCAD binaries
    binaries: path.join(__dirname, '/binary.zip'),

    // output folder where ClassCAD unpacks or is already present (in that case 'binaries' can be undefined)
    output: path.join(__dirname, '/temp/'),

    // name of ClassCAD runtime
    executable: 'CCWebSocketService.exe',

    // ClassCAD ini configuration
    config: 'release.ini',

    // number of ClassCAD instances, default: 1
    instances: 3,

     // public port, default: 8181
    publicport: 8181,

    // optional: internal port used for ClassCAD instances, default: random starting from 9000
    privateport: 9191,

    // allow socketIO to receive binary frames, default: true
    binary: true,

    // log ClassCAD instance stdout, default: false
    stdout: false
});
server.start();

Using the server locally

You are not constrained to remote clients. You can use awv3-node locally as well by creating your own users which can execute and receive commands. The resulting task has a 'results' collection in which all the ClassCAD output will be put. Note that this excludes binary packages for now.

const User = require('awv3-node/modules/user');

// Wait for the server to boot up ...
server.start().then( () => {

    // Create virtual user
    let user = new User(server);
    // Execute command
    user.execute({ command: "Content", action: "getArticle" }).then(results => {
        console.log(results)
    });

});

Middleware

Middleware lets you hook into the servers task-stack. Whenever a user creates a task the resulting object is passed to the middleware, which can change the task or even discard it. Middleware is also allowed to create new tasks on the spot, which can be executed for the original user.

server.use(task => {

    // Intercept 'Content' tasks
    if (task.payload.command === 'Content') {

        // Discard task, meaning it won't be processed at all
        task.discard();

        switch (task.payload.action) {
            case 'getArticle':
                    // Return a promise. This is what the original receiver will resolve to in the end
                    // In this case we execute a different command in the users context, using the same task id
                    // The user will receive these results as if he requested them in the first place
                    return task.user.execute({ command: 'OpenStream', type: 'SAT', data }, task.id)));

                break;
        }
    }

});

REST API

Log in a new user with the /login endpoint. The user will be treated in the same way as a socket.IO user. This has been abstracted in the awv3 library under the /communication/rest class which features the same API as the socketio or the signalr classes.

GET /login/:id

    response => { op: 'system', status: [either 'permitted' or 'rejected'], id: [session ID] }

Log out with the /logout endpoint

GET /logout/:id

    response => "disconnected"

Execute tasks with the /execute endpoint. Note that due to the limitations of REST, there is no server push available. Results will be sent in a single, complete chunk. Another limitation is that requests may time out, should the server be too busy. Such errors need to be handled by the client.

POST /execute/:id
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    },
    method: "POST",
    body: JSON.stringify(command)

    response => Array of results

Custom endpoints

By default awv3-node will create three REST endpoints, /status for a JSON saity-check and /analyzer for debugging and prototyping and /health for load balancing.

It is possible to create more endpoints by using the express API.

// /test
server.express.get('/test', (req, res) => res.type("text/plain").send("hi there!");

// /color?value=#ff00ff
server.express.get('/color', (req, res) => res.type("text/plain").send(`You said: ${req.query.value}`);

// /arg/hello
server.express.get('/arg/:arg', (req, res) => res.type("text/plain").send(`You said: ${req.params.arg}`);

Custom load balancing

Override the [server.health] function to determine the instances health status. The function must return a HTTP status code. Load balancers can probe the /health endpoint to flag single instances as busy, which will remove them from the set of balanced instances until they become active again. The default criteria seeks to flag an instance as not-healthy (status code 503, service unavailable) if 90% of its ClassCAD sessions are busy.

server.health = function() {
    let sessions = Array.from(server.sessions.values());
    let busy = 0;
    for (let session of sessions)
        if (session.tasks.size > 0)
            busy++;

    // If 90% are busy, start flagging the instance as unhealthy
    return busy > sessions.length * 0.9 ? 503 : 200;
};

Farms

A farm is a node instance that hosts a number of ClassCAD processes which can connect to an instance manager. It must bring either ClassCAD binaries or ship files inside the [output] folder.

const Farm = require('awv3-node/farm');

let farm = new Farm({
    // optional: zip that contains ClassCAD binaries
    binaries: path.join(__dirname, '/binary.zip'),

    // output folder where ClassCAD unpacks or is already present (in that case 'binaries' can be undefined)
    output: path.join(__dirname, '/temp/'),

    // name of ClassCAD runtime
    executable: 'CCWebSocketService.exe',

    // local IP, if needed
    ip: undefined,

    // public port, default: 9292
    publicport: 9292,

    // maximal number of ClassCAD instances this farm can hold
    instancesMax: 4
});

It has two APIs, one is functional:

// Boot up ClassCAD instances
farm.start({
    ip: 'http://url', // Servers address
    port: 9191, // Servers private port!
    binary: true,
    debug: false,
    show: false,
    instances: 1
});

The other depends on REST

GET /require/:ip/:port/:instances?

    response => [ array of created processes ]