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 efficiently
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 integer
- 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-models 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-models/model.js
// SensorModel inherits from Model class SensorModel extends Model { constructor() { super(); // add attributes to the model this.add_attr({ id: 0, name: "", hydrometry: 0, temperature: 0 }); } } module.exports.SensorModel = SensorModel; // SensorListModel inherits from Model class SensorListModel extends Model { constructor() { super(); // Array of equipments this.add_attr({ sensors: [] }); } } module.exports.SensorListModel = SensorListModel;
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 spinal-organ-sensor1/index.js should look like this.
// Requirements and connection const spinalCore = require('spinal-core-connectorjs'); const models = require('../spinal-models/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 function 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.name.get() + ": data has changed"); simulate(sensor); }, 1000); }; // Finds a Sensor in the SensorList, if it doesn't exist, creates it function getSensorById(list, id) { let item; for (let i = 0; i < list.sensors.length; i++) { if (list.sensors[i].id.get() === id) { item = list.sensors[i]; break; } } if (typeof item === "undefined") { item = new models.SensorModel(); item.id.set(id); item.name.set("sensor" + id); list.sensors.push(item); } return item; } // This function will be called if the list is successfully loaded function onSuccess(list) { simulate(getSensorById(list, 0)); }; // This function will be called if the list cannot be loaded function onFailure() { const list = new models.SensorListModel(); const item = getSensorById(list, 0); spinalCore.store(conn, list, "List", () => { simulate(item); }); }; spinalCore.load(conn, "List", onSuccess, onFailure);
Run spinal-organ-sensor1/index.js 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 spinal-organ-sensor2/index.js should look like this.
// Requirements and connection const spinalCore = require('spinal-core-connectorjs'); const models = require('../spinal-models/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 function 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.name.get() + ": data has changed"); simulate(sensor); }, 1000); }; // Finds a Sensor in the SensorList, if it doesn't exist, creates it function getSensorById(list, id) { let item; for (let i = 0; i < list.sensors.length; i++) { if (list.sensors[i].id.get() === id) { item = list.sensors[i]; break; } } if (typeof item === "undefined") { item = new models.SensorModel(); item.id.set(id); item.name.set("sensor" + id); list.sensors.push(item); } return item; } // This function will be called if the list is successfully loaded function onSuccess(list) { simulate(getSensorById(list, 1)); }; // This function will be called if the list cannot be loaded function onFailure() { const list = new models.SensorListModel(); const item = getSensorById(list, 1); spinalCore.store(conn, list, "List", () => { simulate(item); }); }; spinalCore.load(conn, "List", onSuccess, onFailure);
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 how 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 restart launch.config.json.
Your .app.json should look like this:
{ "apps": [ { "name": "spinal-core-hub", "script": "spinalhub.js", "cwd": "./nerve-center/" }, { "name": "sensor1", "script": "index.js", "cwd": "./spinal-organ-sensor1/" }, { "name": "sensor2", "script": "index.js", "cwd": "./spinal-organ-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 efficiently
This tutorial will be continued in Basic Automate Tutorial.