SpinalGraph : Create your first Graph

Table of contents


Goal

By following this tutorial, you will able to create a simple SpinalGraph, add equipment in it, and visualize the SpinalGraph ( also call Graph ) in viewer.


System Schema

the schema system is simple, we use Drive UI for create Digital Twin, Admin UI / Viewer UI for visualize the Graph we are going to create and a simplistic organ for initialize all of that thing.


Data Schema

We gonna create a simplistic Graph, with one Building, three Floor and one Equipment in a Room in each Floor.


Create and Init project Files

To begin, create the spinal-graph-tuto folder and  with the terminal move to this folder and type the commands below.

npm init -y

The commands above allow you to initialize the project , if you check in the project folder, you will notice that there is a package.json file, replace its content with the code below  and install the dependencies by typing the command npm install

{
  "name": "spinal-graph-tuto",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "spinal-browser-viewer": "https://github.com/spinalcom/spinal-browser-viewer",
    "spinal-browser-admin": "https://github.com/spinalcom/spinal-browser-admin",
    "spinal-browser-drive": "https://github.com/spinalcom/spinal-browser-drive"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

 If you need more details about package, let's see tutorials about it here,

Don't forget to modify .config.json file, we need to add info for the spinal-organ-forge, see here what we need, and start a new BIMObject, download .rtv file and create Digital Twin, we gonna add the Graph to this Digital Twin, so, for better convenience, with the file generated, rename rac_basic_sample_project (file given as an example in the tutorial) it to basic,

Create Graph

now let's create a new folder and init a package.json in it,

mkdir Organ-create-graph && cd Organ-create-graph
npm init -y

In the package.json, add the dependencies :

{
   "name": "dummy-graph-organ",
   "version": "1.0.0",
   "description": "",
   "main": "index.js",
   "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
   },
   "keywords": [],
   "author": "",
   "license": "ISC", 
   "dependencies": {
    "spinal-lib-forgefile": "git+https://github.com/spinalcom/spinal-lib-forgefile.git",
    "spinal-core-connectorjs": "git+https://github.com/spinalcom/spinal-core-connectorjs.git",
    "spinal-model-graph": "git+https://github.com/spinalcom/spinal-model-graph.git"
  }
}

Install with npm install install command

Configuration

Create a file named config.js and write this in it

module.exports = {
  spinalConnector: {
    user: 168,
    password: 'JHGgcz45JKilmzknzelf65ddDadggftIO98P',
    host: 'localhost',
    port: 7777,
    protocol: 'http'
  },
  file: {
    path: '/__users__/admin/basic'
  }
};

You can see common info about SpinalConnector, and the file info store the path of your new Digital Twin previously created and renamed

Now let's create file and function to create graph and store this in a Digital Twin basic in a new index.js

var spinalCore = require('spinal-core-connectorjs');
var forgeFile = require('spinal-lib-forgefile');
var config = require('./config');

var spinalgraph = require('spinal-model-graph');

const connect_opt =
  `http://${config.spinalConnector.user}:${config.spinalConnector.password}@${
    config.spinalConnector.host}:${config.spinalConnector.port}/`;

var conn = spinalCore.connect(connect_opt);

/**
 * This function create a context VirtualNetwork if don't exist, get it if exist
 * and return it;
 */
let createOrGetContext = function(_graph) {
  return new Promise((resolve, reject) => {
    _graph.getContext("DummyContext").then(async context => {
      if (typeof context !== "undefined") {
        return resolve(context);
      }

      let dummy = new spinalgraph.SpinalContext(
        "DummyContext",
        "DummyContext",
      );

      try {
        _graph.addContext(dummy);
        dummy = await _graph.getContext("DummyContext");
        resolve(dummy);
      } catch (e) {
        reject(e);
      }
    });
  });
};

/**
 * This function create a graph if don't exist, get it if exist and return it;
 */
let getGraph = _file => {
  return new Promise(function(resolve) {
    if (typeof _file.graph != 'undefined') return resolve(_file.graph);

    var _graph = new spinalgraph.SpinalGraph();
    _file.add_attr({
      graph: _graph
    });
    resolve(_graph);
  });
};

let getFileAnCreateNetwork = (_file) => {
  getGraph(_file).then(_graph => {
    buildDummy(_graph);
  });
};

/**
 * This function fill your graph with custom node, room, floor, equipment...
 */
let buildDummy = async function(_graph) {
  let context = await createOrGetContext(_graph);
  let promises = [];
  promises.push(context.addChildInContext(new Model(), 'HasFloor'));
  promises.push(context.addChildInContext(new Model(), 'HasFloor'));
  promises.push(context.addChildInContext(new Model(), 'HasFloor'));
  await Promise.all(promises);

  const children = await context.getChildren();
  promises = [];
  for (let i = 0; i < children.length; i++) {
    children[i].info.name.set('Floor' + i);
    const room = new spinalgraph.SpinalNode('Room' + i);
    const equipment = new spinalgraph.SpinalNode('Equipment' + i);
    equipment.info.add_attr({
      status: true
    });
    promises.push(room.addChildInContext(
      equipment, 'HasEquipment', spinalgraph.SPINAL_RELATION_TYPE,
      context));
    promises.push(children[i].addChildInContext(
      room, 'HasRoom', spinalgraph.SPINAL_RELATION_TYPE, context));
  }
  return Promise.all(promises);
};


// LoadFile, you already know this function, it's explain in first tutorial
spinalCore.load(conn, config.file.path, getFileAnCreateNetwork, () => {
  const _file = new forgeFile.ForgeFileItem();
  spinalCore.store(
    conn, _file, config.file.path, getFileAnCreateNetwork.bind(this,
      _file)
  );
});

The first part of this file is already know if you are done the basic tutorial

getGraph return the Graph if it exist, or create it

createOrGetContext get the graph and return a Context, or create it

buildDummy fill your graph with 3 Floor, in each Floor he creates one Room, and in each Room one Equipment

getFileAnCreateNetwork  call function above and create ForgeFileItem, Graph or Context if they do not exist

We can notice we are set status attribut, in each equipment.

More details about SpinalGraph Method

You can easily found more details in a API section, /wiki/spaces/DOC/pages/174260318

Visualize the graph

to visualize the graph, open the view at http://localhost:7777/html/drive#!/home 

In this tutorial we have bind the graph to the ForgeFileItem, so drag and drop the Digital Twin previously created (and renamed basic) to your inspector

Conclusion

In this short tutorial you see how to create a basic graph with context, bind this to your ForgeFileItem, and some floor, room or equipment and visualize it in viewer.