This is a api library that provides a connection and understandable api layer for a certain HMIELite system.
This HMIELite system must be connected to QUERCUS CNC machine.
There are two scenarios where libfagorclient is designed to be used:
⚠️️(This step is only needed if libfagorclient is used from scratch)
This libfagorclient library is distributed in two flavors.
Right now libfagorclient client is distributed using a tar.gz file. The library can be downloaded clicking here:
📦 Download libfagorclient-latest.tgz
Both bundles (umd and es module), include every dependecy needed to be able to work with the CNC Server.
Shortly the package will be available thought a private npm repository; so no manual interaction will be needed in order to install or update this library.
The .tgz file distributed is a npm-compatible packed file.
.
└── package
├── dist
│ ├── libfagorclient.es5.js
│ ├── libfagorclient.umd.js
│ ├── polyfill.js
│ └── types
│ ├── /**
│ │ Typing files for typescript usage
│ │ **/*.d.ts
│ │ ........
│ └── **/
├── package.json
└── README.md
The minimal files needed to setup a working CNC UI are these:
⚠️(This step is only needed if libfagorclient is used from scratch)
Both bundles are likely to be used just by uncompressing the file, and including them on the HTML:
<script nomodule src="./lib/polyfill.js"></script>
<script nomodule src="./lib/libfagorclient.umd.js"></script>
<script nomodule>
// libfagorclient. object is created on global context
var conn = new libfagorclient.CommHmiNodeServer(); // Implements the connection process with the main HMI Node Server
var cnc = new libfagorclient.Cnc(conn); // CNC class
</script>
Or we can just import them using ES Modules (just evergreen browsers).
<script type="module">
import {
CommHmiNodeServer,
Cnc
} from './lib/libfagorclient.es5.js'
const conn = new CommHmiNodeServer(); // Implements the connection process with the main HMI Node Server
const cnc = new Cnc(conn); // CNC class
</script>
This is a not recomended way of working, since no package manager is used.
If we are build a modern web application, most likely you would be using npm or yarn. Download the .tgz file, and place it anywhere on your computer.
Then just use npm install as if you were installing any othe dependency on your proyect.
$ npm install c:\Downloasd\libfagorclient-latest.tgz
The library 'libfagorclient' will be available from within your proyect, and you will be able to start using it:
import {
CommHmiNodeServer,
Cnc
} from 'libfagorclient'
const conn = new CommHmiNodeServer(); // Implements the connection process with the main HMI Node Server
const cnc = new Cnc(conn); // CNC class
Please, be aware the dependency wont be copied to your VCS (git, snv, ...); a relative path will be added to your pacakge.json file; be sure the libfagoclient-latest.tgz file location is shared within your team:
{
"dependencies": {
"libfagorclient": "file:../libfagorclient/libfagorclient-0.0.1.tgz",
}
}
We could use the a remote URL but just from within Fagor Automation's LAN, since the .tgz file is not accesible from the Internet:
$ npm install http://new-hmi.gitlab.fagorautomation.net/libfagorclient/libfagorclient-latest.tgz
⚠️(This step is only needed if libfagorclient is used from scratch)
First of all, we need an instance of CommHmiNodeServer. This instance is used to connect to a HMIElite system and do the authentication process.
//This is only a example of how we can import classes and enums from libfagorclient.
import {
CommHmiNodeServer,
Cnc,
CncKernelType
} from './lib/libfagorclient.es5.js'
const conn = new CommHmiNodeServer(); // Implements the connection process with the main HMI Node Server
Once conn object is instantiated, we can connect it to remote point filling the request with aproppiate connection data:
// Connect to remote endpoint
conn.connect({
host: 'localhost',
port: 3000
username: 'user',
password: 'user'
});
CommHmiNodeServer class emits online event when whole connection chain has been accomplished. At this stage, all services are operative and we can perform any operation that involves a communication with the cnc. If any link is disconnected emits offline event and automatically schedules a reconnection timeout every 5 seconds.
conn.on('online', () => {
//Every operation must be performed in this scope.
});
conn.on('offline', () => {
//Every operation related to disconnection must be performed in this scope.
});
Further information about connection, authentication and related events can be found in Connection section.
But conn instance only allows the execution of services related to connection, it is a generic instance that should be used in another specialized classes that are exported by libfagorclient.
For now, these are the clases exported by this library:
⚠️(This step is only needed if libfagorclient is used from scratch)
So, if we want to instantiate a Cnc or Plc object, we must inject conn object in the constructor of these classes.
const cnc = new Cnc(conn); // CNC class
const plc = new Plc(conn); // PLC class
Notice that Cnc constructor by default creates a Real kernel instance, if we want to create a simulated instance we must add an argument to constructor using CncKernelType enum.
const cncSimulated = new Cnc(conn, CncKernelType.SIMULATED); //Cnc simulated class
Cnc class exports a cnc data model which represents a logical view of the machine we have connected to. This implies that all physical elements of the machine such spindles and axes or logical such channels can be inspected anytime and every change on these elements will be notified in real time.
In order to avoid extra cpu usage, the cnc model is created upon request.
cnc.requestModel();
When model is ready, Cnc instance emits created event, and whenever the model is updated emits updated.
Plc class exports the plc data model with all resources values. All entities can be inspected anytime and every change on these elements should be notified in real time.
The plc Model will contain a updated representation of the resources of PLC:
In order to avoid extra cpu usage, the plc model is created upon request.
plc.requestModel();
When model is ready, Plc instance emits created event, and whenever the model is updated emits updated.
Once the model is created we can inspect it, for example, we can achieve every plc resource name and the value of a specific registry and mark.
plc.on('created', ()=> {
// This command will output every plc resources name
console.log(Object.keys(plc.getModel()));
// This command will output R1024 register's value
console.log(plc.getModel().r['1024']));
// This command will output I24 input's value
console.log(plc.getModel().i['24']));
// This command will whole plc resources object
console.log(Object.keys(plc.getModel())));
});
When execution cycle must be closed, we should disconnect conn instance but it is recommnended to disconnect fron plc model updates before to avoid unnecessary execution cycles in main server.
plc.disconnectFromModel();
conn.disconnect();
Be aware of removing every listener created before to avoid possible memory leak and performance degradations.
Once the model is created we can inspect it. For example, for cnc model we can achieve the logical ids of axes of the first channel.
let currentAxes = [];
cnc.on('created', (cncModel) => {
cncModel.getChannel(1).axes.map(ax => {
currentAxes.push(ax.logicId);
});
})
We can use stored logical ids to schedule a polling timer to get axis positionment data and paint them.
// We choose a pooling in order to update cnc values
// an "updated" event is send on cnc, but this approach is easier.
pollingTimer = setInterval(() => {
currentAxes.map((axLogicId) => {
//We retrieve axis reference by logic id.
const ax = cnc.getAxis(axLogicId);
console.log(`
name: ${ax.name}
pos: ${ax.pos.external}
flwe: ${ax.flew.external}
parked: ${ax.parked}
`)
});
}, 60);
Further information about cnc data model can be found in Cnc Model and Services section.
But what about the interaction with Cnc? Cnc class exposes some services to allow us to operate with the cnc and also emits some real time events to warn every relevant change in cnc kernel state.
In this example, we are going to enter block mode in the first channel (the cnc by default is launched in program mode, so that the press of a start will launch the selected program execution). Once block mode is activated, we can set a execution block that will be executed when start is pressed.
Related to this process, Cnc emits two differents events:
We must wait until cnc operation mode is set to BLK_SELEC to program a executing block. In this example we will program 'G0X100' block.
Further information about cncEvents can be found in Cnc Events section.
//we retrieve first channel
const channel = cnc.getChannel('1');
console.log(`About to execute [cnc.getChannel('1').cncServices.enterBlockMode()]`);
channel.cncServices.enterBlockMode().then(console.log);
// EVENTS
// When Cnc emits CncStateEvent, we print it.
cnc.on('CncStateEvent', event => {
if ( event.data.state === 'EXEC') {
console.warn('EXEC');
} else if ( event.data.state === 'STOP') {
console.warn('STOP');
} else if ( event.data.state === 'NO_READY') {
console.warn('NO_READY');
} else if ( event.data.state === 'ERROR') {
console.warn('ERROR');
} else {
console.warn('READY');
}
});
// When Cnc emits CncModeEvent, we program 'G0X100' block.
cnc.on('CncModeEvent', event => {
console.log(`Cnc operation mode has changed: ${event.data.mode}`);
if(event.data.mode === 'BLK_SELEC') {
console.log(`About to execute [cnc.getChannel('1').cncServices.setBlock('G0X100')]`);
channel.cncServices.setBlock('G0X100').then(console.log);
}
});
We have implemented a reactive-event system for some model properties. It allows developer to dynamically create events on runtime, based on changes for some propeties across the CNC models (channels, spindles, axes and most of the models contained on them).
The system implements debouncing on the emision to prevent an event-flood on highly changeable values. (default value is 60ms)
const axis = cnc.getAxis('X');
const listenerForAxis = axis.createListener(['diameters', 'inUse' 'ppos', 'pos', 'flwe', 'parked']);
console.log("Creating dynamic listener for axis X's properties: diameters', 'inUse' 'ppos', 'pos', 'flwe', 'parked");
listenerForAxis.start((axis, propsChanges) => {
console.log(`The properties ${propsChanges.join(', ')} have changed on `, axis);
}, {debounceTime: 100});
// The emission can be stopped anytime
listenerForAxis.stop()
// And it should be deleted when no longer needed (to avoid memory-leaks)
axis.deleteListener(listenerForAxis);
Reactive properties are documented under the @Listenable() decorator on the following objects:
Any property placed above the @Listenable() decorator might be used to create a reactive event based on changes.
When a web application or integrated customized app must be destroyed, it is important to notice that all the allocated resources must be removed and freed.
These resources could be event listeners, timers or global objects. In order to allow garbage collector to collect them, all the references must be cleared or disconnected.
cnc.disconnectFromModel();
cnc.removeAllListeners('CncModeEvent');
cnc.removeAllListeners('CncStateEvent');
conn.disconnect();
We have set up a simple sample application to show how build a new web application using a fresh instance of libfagorclient:
The library is set up to be able to work with typescript. Every type used within the library is exported directly from 'libfagorclient'.
Example:
import {
Cnc,
CommHmiNodeServer,
Diagnosis,
LiveStream,
Plc,
enums as LFCEnums,
interfaces as LFCInterfaces,
} from 'libfagorclient';