For those who aren’t JavaScript addicts yet, here is another interesting opportunity to become more familiar with the most trendy programming language of the moment!
This post is derived from an app originally written by my colleague Jan Liska, from Autodesk Consulting EMEA. Thanks Jan!
Jan was playing with a technology named Node.js. If you haven’t heard about it yet, I encourage you to take a look at their website. Basically Node.js allows you to have your server backend implemented in JavaScript. It gets a lot of attention recently for commercial or non-commercial purposes. It also worth mentioning it is absolutely free…
Here is how the authors define it:
Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.
Application description:
Here is a short description about how the app is working:
- The client connects to the server through a socket using a browser.
- The server has a local repository of Inventor files. It parses the existing files in this repository and returns that info to the client.
- The client selects a specific file and request the data for the file.
- The server runs a worker task, which is an exe using the in-process Apprentice component.
- Apprentice opens the file provided by the server and extracts some data from it, in that case a couple of iProperties but also non trivial data such as a snapshot picture of the file, it notifies the server which will return the data to the client in order to be displayed in the browser.
Application Highlights:
Here are the main points to discuss concerning the app
The client and server are using a socket connection to communicate together. This is the best approach in term of efficiency as it induces far less overhead than using REST calls for example. The server recognize two socket messages: “getModels” when the client is requesting the list of available models and “getModelDataByName” when the client is requesting data on a selected model:
// connects socket to server
var io = socketio.listen(server, { log: false });
// socket connection callback
io.sockets.on('connection', function (socket) {
console.log('Incoming socket connection: ' + socket.id);
// new socket client connected
socket.on('connect', function () {
});
// socket client disconnected
socket.on('disconnect', function () {
});
// list of models requested from client
socket.on('getModels', function () {
console.log('socket.on \'getModels\'');
models.getModels(function (models) {
// send reply to client
socket.emit('models', models);
});
});
// data for specific model requested from client
socket.on('getModelDataByName', function (model) {
console.log('socket.on \'getModelDataByName\': ' + model);
models.getModelDataByName(model, function (data) {
// send reply to client
socket.emit('modelData', data);
});
});
});
The “models.getModels” and “models.getModelDataByName” are two simple helpers methods: the first one iterates through a specific local folder on the server using Node.js file system API “fs.readdir”. The second one is spawning the Apprentice process and waits for it to return. The Apprentice output is written into a json formatted temp file which is then read by the server app “fs.readFile”, data is send back to the client through the socket and file is deleted “fs.unlink”:
exports.getModels = function (callback) {
var folderPath = path.resolve(__dirname + '/models').toString();
fs.readdir(folderPath, function (err, files) {
if (err) throw err;
var models = [];
for (var i = 0; i < files.length; i++) {
var fileName = files[i];
if ((path.extname(fileName) == ".ipt") ||
(path.extname(fileName) == ".iam")) {
var model = { id: i, name: files[i] };
models.push(model);
}
}
callback(models);
});
};
exports.getModelDataByName = function (model, callback) {
var workerAppPath = path.resolve(
__dirname + '/../WorkerApp/bin/Release/WorkerApp.exe').toString();
// generates a temp filename for worker output
var tempName = 'tmp' + crypto.randomBytes(4).readUInt32LE(0);
var inputFile = path.resolve(
__dirname,
'models/' + model).toString();
var outputFile = path.resolve(
__dirname,
'data/' + tempName + '.json').toString();
var params = [inputFile, outputFile];
// spawn worker process
var worker = process.spawn(workerAppPath, params);
worker.stdout.on('data', function(data) {
console.log('Worker output: ' + data.toString());
});
// worker finished callback
worker.on('exit', function (code) {
console.log('Worker finished with code: ' + code.toString());
// read output file to retrieve model data
if (code == 0) {
fs.readFile(outputFile, 'utf8', function (err, data) {
if (err) {
console.log('Error log:' + JSON.stringify(err));
}
else {
var modelData = JSON.parse(data);
callback(modelData);
// delete temp file from disk
fs.unlink(outputFile, function (err) {
if (err) {
console.log(JSON.stringify(err));
}
});
}
});
}
});
}
On the Apprentice Exe side, I am using JSON.Net which is a nice and easy JSON library for .Net. Serializing my output data to JSON and writing the result to a file is as simple as it can get:
string json = JsonConvert.SerializeObject(
model,
Formatting.Indented);
File.WriteAllText(outputFile, json);
The client is an html page with a bit of JavaScript and JQuery in order to perform server calls and the UI actions, such as dynamically modifying the table where the data get displayed but no big deal if you are familiar with those technologies.
Attached with that posts is the complete project you can try on your side.
Prerequisites to run the app:
- Download and install Node.js: http://nodejs.org/download
- If you haven’t Inventor installed on the machine, the minimum requirement is to download and install InventorView. It will install and enable Apprentice at the same time.
- Run “npm install” from the “Server” directory of my project. This will retrieve any needed dependency for the Node.js code to run properly.
- Run the “run server.bat” file to launch the Node.js Server app
- Connect to “localhost:3000/nodesrv” with a browser to use the app
Deploying the app on the cloud would be as well pretty straightforward. You will need to follow the exact same steps than for a local deployment, taking care about opening the ports you have chosen to run your server, in my case 3000. You will also need to change the client code in order to reflect the public IP of your server:
(code from client.js)
// Need to change that to server name
// if server isn't running locally
var socket = io.connect(location.hostname);
Comments
You can follow this conversation by subscribing to the comment feed for this post.