In this tutorial you learn how to :
- Create a model or a Digital twin of your equipment sensors.
- Create organs that simulate your sensors behavior.
- Launch your organs with effectively.
System architecture:
For this tutorial, you just need the basic requirements
In this part you’re going to create two sensor models:
- Sensor, a model with 4 attributes:
- id : unique int
- name : string
- hydrometry : integer
- temperature : integer
- SensorList, a model with 1 attribute:
- sensors : array of sensors
- sensors : array of sensors
Create your spinal system and digital twin by following these steps:
- Create a new spinal-system from the browser-admin template like it’s explained in the in the section "Creating a new Spinal System" in the Getting Started tutorial.
- Create a folder called spinal-model-equipments to store the different models. Within this folder create a file named models.js.
- Complete model.js:
- Create the sensor model.
- Extend the sensor model with the Model class from spinalCore.
- Export the sensor model.
- Create the sensor list.
- Extend the sensor list with the Model of spinalCore.
- Export the sensor list.
Your model file should look like this:
spinal-model-equipments/model.js
// Sensor model inherits from Model class Sensor extends Model { constructor() { super(); // add attributes to the model this.add_attr({ id: 0, name: "", hydrometry: 0, temperature: 0 }); } } module.exports.Sensor = Sensor; // SensorList model inherits from Model class SensorList extends Model { constructor() { super(); // Array of equipments this.add_attr({ sensors: [] }); } // Creates a new sensor and add it to the list addItem() { const item = new Sensor(); item.id.set(0); item.name.set("sensor0"); this.sensors.push(item); } } module.exports.SensorList = SensorList;
In this part your are going to instantiate a list of 2 sensors digital twin in the hub database and create two "organs" of the system that will simulate the sensor behavior.
Each organ will simulate changes of hydrometry and temperature.
- Create a new folder spinal-organ-sensor1 and within it a file named index.js.
- in the index.js file:
- Require spinal-core.
- Get the connection parameter from the config.js file in the main folder
- Create a function addItem. This function create, set and add an item to the list given in argument.
- Create a function simulate. This function simulate the value of the sensor.
- Create a function onSuccess. This function will be executed if the loading of ‘List’ is successful.
- Create a function onFaill. This function will be executed if the loading of ‘List’ fail.
- Load list from spinal-core.
- Create a new folder spinal-organ-sensor2
- Copy paste the index.js into sensor2 folder and do the appropriate modifications.
- If you have and API to retrieve the real data of your sensor use it to send real data to the graph.
- You can run every sensor independently by running the corresponding index.js (node index.js)
Your file sensor1/index.js should look like this.
// Requirements and connection const spinalCore = require('spinal-core-connectorjs'); const models = require('../spinal-model/models.js'); console.log("Configuration Environment not found, using default config"); process.env.SPINALHUB_PORT = 7777; process.env.SPINALHUB_IP = "127.0.0.1"; process.env.SPINAL_USER_ID = 168; process.env.SPINAL_PASSWORD = "JHGgcz45JKilmzknzelf65ddDadggftIO98P"; const conn = spinalCore.connect(`http://${process.env.SPINAL_USER_ID}:${process.env.SPINAL_PASSWORD}@${process.env.SPINALHUB_IP}:${process.env.SPINALHUB_PORT}/`); // Gives random values to the hydrometry and temperature of a sensor simulate = (sensor) => { const hydro = Math.floor(Math.random() * 100); const degrees = Math.floor(Math.random() * 30); sensor.hydrometry.set(hydro); sensor.temperature.set(degrees); // Repeats every second setTimeout(() => { console.log("sensor 1: data has changed"); simulate(sensor); }, 1000); }; function findIndexById(id, list) { if (list.sensors.length <= 0) return -1; let i; for (i = 0; i < i < list.sensors.length && list.sensors[i].id !== id; i++); if (i < list.sensors.length) return i; else return -1; }; // This function will be called if the list is successfully loaded function onSuccess(list) { const index = findIndexById(0, list); console.log("success 2: index: ", index); if (index !== -1) simulate(list.sensors[index]); else list.addItem(); }; // This function will be called if the list cannot be loaded function onFailure() { const list = new models.SensorList(); spinalCore.store(conn, list, "List", () => { list.addItem(); }); }; spinalCore.load(conn, "List", onSuccess, onFailure);
Run sensor1 and go to the admin UI and put 'List' into the inspector and you should see the hydrometry and temperature of sensor0 change every second.
Your file sensor2/index.js should look like this.
// Requirement and connection const spinalCore = require('spinal-core-connectorjs'); require('../spinal-model/models.js'); const connection_string = require("../config"); const conn = spinalCore.connect(connection_string); // Creates a new sensor and add it to the list addItem = (list) => { const item = new Sensor(); item.id.set(1); item.name.set("sensor1"); list.sensors.push(item); simulate(item); }; // Gives random values to the hydrometry and temperature of a sensor simulate = (sensor) => { const hydro = Math.floor(Math.random() * 100); const degrees = Math.floor(Math.random() * 30); sensor.hydrometry.set(hydro); sensor.temperature.set(degrees); // Repeats every second setTimeout(() => { console.log("sensor 2: data has changed"); simulate(sensor); }, 1000); }; findIndexById = (id, list) => { if (list.sensors.length <= 0) return -1; let i; for (i = 0; i < i < list.sensors.length && list.sensors[i].id !== id; i++); if (i < list.sensors.length) return i; else return -1; }; //this function will be called if the list is successfully load onSuccess = (list) => { const index = findIndexById(1, list); console.log("success 2 : index:", index); if (index !== -1) simulate(list.sensors[index]); else addItem(list); }; //this function will be called if the list is failed to load onFail = () => { const list = new SensorList(); spinalCore.store(conn, list, "List", () => { addItem(list); }); }; spinalCore.load(conn, "List", onSuccess, onFail);
You might have noticed that running every organ independently is painful.
If you wonder why it’s so painful it’s because you are not supposed to do that :p.
In this part you will learn a to run you organs efficiently.
- Take a look at .app.json
- add dependency to your organs.
- name: The name they will be identified by pm2
- script: The name of the script to be executed to launch the organ
- cwd: The location of the script (relative to the root of the project)
- run the commande pm2 start launch.config.json.
.apps.json contains the organs that should be launched. They are identified by:
- name: The name they will be identified by pm2
- script: The name of the script to be executed to launch the organ
- cwd: The location of the script (relative to the root of the project)
Your .app.json should look like this:
{ "apps": [ { "name": "spinal-core-hub", "script": "spinalhub.js", "cwd": "./nerve-center/" }, { "name": "sensor1", "script": "index.js", "cwd": "./sensor1/" }, { "name": "sensor2", "script": "index.js", "cwd": "./sensor2/" } ] }
pm2 will now show you sensor 1 and 2 along with spinal-core-hub.
Conclusion
In this tutorial you learned:
- how to create complex models
- how to organize your data coming from multiple organs
- how to launch you your organs effectively
This tutorial will be continued in Basic Automate Tutorial.