I am developing a web application in node.js to collect data from devices on a network using snmp. This is my first real encounter with node.js and javascript. In the app each device will be manipulated through a module I named SnmpMonitor.js. This module will maintain basic device data as well as the snmp and database connection.
One of the features of the app is the ability to constantly monitor data from smart metering devices. To do this I created the following code to start and stop the monitoring of the device. It uses setInterval to constantly send a snmp get request to the device. Then the event listener picks it up and will add the collected data to a database. Right now the listener just prints to show it was successful.
var dataOIDs = ["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"];
var intervalDuration = 500;
var monitorIntervalID;
var dataCollectionEvent = "dataCollectionComplete";
var emitter = events.EventEmitter(); // Uses native Event Module
//...
function startMonitor(){
if(monitorIntervalID !== undefined){
console.log("Device monitor has already started");
} else {
monitorIntervalID = setInterval(getSnmp,intervalDuration,dataOIDs,dataCollectionEvent);
emitter.on(dataCollectionEvent,dataCallback);
}
}
function dataCallback(recievedData){
// receivedData is returned from getSnmp completion event
// TODO put data in database
console.log("Event happened");
}
function stopMonitor(){
if(monitorIntervalID !== undefined){
clearInterval(monitorIntervalID);
emitter.removeListener(dataCollectionEvent,dataCallback);
} else {
console.log("Must start collecting data before it can be stopped");
}
}
//...
I also have a test file, test.js, that requires the module, starts monitoring, waits 10 seconds, then stops it.
var test = require("./SnmpMonitor");
test.startMonitor();
setTimeout(test.stopMonitor,10000);
My problem is that the setInterval function in startMonitor() is not being run. I have tried placing console.log("test"); before, inside, and after it to test it. The inside test output never executes. The monitorIntervalID variable is also returned as undefined. I have tested setInterval(function(){ console.log("test"); },500); in my test.js file and it runs fine with no issues. I feel like this is a noobie mistake but I just can't seem to figure out why it won't execute.
Here is a link to the entire module: SnmpMonitor.js
I not sure exactly what was wrong but I got it to work by overhauling the whole class/module. I thought the way I had it was going to allow me to create new monitors objects but I was wrong. Instead I created two functions inside the monitor file that do the same thing. I changed the start function to the following.
SnmpMonitor.prototype.start = function() {
var snmpSession = new SNMP(this.deviceInfo.ipaddress,this.emitter);
var oids = this.deviceInfo.oids;
var emit = this.emitter;
var duration = this.intervalDuration;
this.intervalID = setInterval(function(){
snmpSession.get(dataCollectionEvent,emit,oids);
},duration);
};
The setInterval function seems to work best when the callback function is set inside an anonymous function, even though technically you can pass it directly. Using the this. notation I created some class/module/function variables (whatever its called in js) that are in scope of the whole class. For some reason the variables accessed through this. do not work so well when directly in a function or expression so I created temp variables for them. In my other version all the variables were global and js doesn't seem to like that.
Related
I’m struggling with making Javascript repeat this code block. It's for a code-operated Phidget switch and works just the once to turn an electronic relay on and off (with a timer for duration) ("Phidget22" is the Node package specific to the device).
I can't find a working method to make the process repeat itself.
Below is the working code, with explanatory notes for what the stages do.
var phidget22 = require('phidget22');
function runExample() {
//Create your Phidget channels
var digitalOutput0 = new phidget22.DigitalOutput();
//Set addressing parameters to specify which channel to open (if any)
digitalOutput0.setHubPort(2);
digitalOutput0.setDeviceSerialNumber(606877);
//Assign any event handlers you need before calling open so that no events are missed.
//Open your Phidgets and wait for attachment
digitalOutput0.open(5000).then(function() {
//Do stuff with your Phidgets here or in your event handlers.
digitalOutput0.setDutyCycle(1);
setTimeout(function () {
//Close your Phidgets once the program is done.
digitalOutput0.close();
process.exit(0);
}, 3000);
});
}
Not sure if I get the idea, but perhaps setInterval would work for you.
I'm beginner to NodeJS, so I'm not entirely sure what the best method to achieve this would be. Basically I want to create a global variable with a string, for instance 'USD', that would get updated whenever my 'set currency' event is fired. I want it to remain that way until the event is called again.
I am using EventEmitter to fire off some events, in one of my files I have the following.
var event = require('./events');
if (msg.content.includes('!currency set currency')) {
split = msg.content.split(' ');
event.emit('setCurrency', split[3])
}
And then inside the events file I'm doing something like the following.
var exchangePref;
var event = new events.EventEmitter();
event.on('setExchange', (exchange) => {
exchangePref = exchange;
return exchangePref;
});
modules.exports = event;
I understand that re-writing the variable inside a callback isn't going to do what I need it to do, but I'm quite lost with how to achieve what I need it to do due to the modules.exports = event part at the bottom, the calling function simply never gets the data. I've played around with creating a constructor, but even still I couldn't get it to work.
Any suggestions/ideas would be greatly appreciated.
I wouldn't use event emitter for this. Instead create a module along the lines of:
var exchangePrefs = { currency: "JPY" };
module.exports = {
setCurrency : function(newVal){ exchangePrefs.currency = newVal; },
getCurrency : function(){ return exchangePrefs.currency; }
};
Then in your various other modules you just:
require('./mymodule').setCurrency('USD');
and somewhere else
var currency = require('./mymodule').getCurrency();
I'm sure it can be made prettier, but I think you get the point. For almost all intents and purposes modules work like singletons. There are some gotchas, but nothing you'll run into too often. (Singleton pattern in nodejs - is it needed?)
Personally I'd use some sort of data persistence in the exchangePref-module just for peace of mind. Like redis, or saving to a json-file.
I'm trying to get a handle on web workers when I came across a very peculior behaviour. For some reason it's terminated after a few seconds, even though I have code in it that's running.
Here's my code;
Main JavaScript-file:
$(document).ready(function () {
var worker = new Worker("js/TestWorker.js");
worker.addEventListener('message', function (event) {
console.log(event.data);
});
worker.addEventListener('error', function (event) {
console.log(event);
});
});
Worker file:
(function () {
var updateCounter = 0;
var updater = function () {
updateCounter += 1;
console.log("Update counter: " + updateCounter);
postMessage("test");
setTimeout(updater, 10000);
};
updater();
})();
As stated, the worker just stops functioning after a few seconds, 10-20seconds or so.
But if I add this piece of code to my main JavaScript-file;
var check = function () {
var localWorker = worker;
// setTimeout(check, 1000);
};
// setTimeout(check, 1000);
The worker works as intended. The setTimeout-calls aren't needed either, hence why they're commented out. (Note that I can just aswell replace the assignment with worker.length or something similar and it will still work just fine.
Can someone explain this behaviour? Is the worker getting terminated and (erroneously) garbage-collected by the browser or is something else happening here that I'm missing?
Worth to note is that my browser (Chrome) isn't outputing any errors or warnings to the console either.
EDIT: The same behaviour is observed whether the code is executed inside an anonymous function or not.
EDIT2: If I place the worker variable in the global scope it does not get terminated prematurely. What might be the reason for this?
Some research shows that while web workers are supposed to function as you expect (i.e. won't be perceptibly garbage collected), there are some known issues in Chrome which mean you can't rely on that behaviour.
Of specific interest would be this very similar bug: https://bugs.chromium.org/p/chromium/issues/detail?id=572225 which in turn references a more underlying bug: https://bugs.chromium.org/p/chromium/issues/detail?id=572226
It seems to be due to an attempt to garbage collect workers which cannot possibly perform any future activities (in which case the garbage collection would be undetectable, as it's supposed to be), but a flaw in the logic for detecting this state means that any pending activities which aren't directly related to responding to an incoming message will be ignored.
Basically, while you should be able to assume web-workers behave like DOM nodes which can only be removed explicitly, in practice (for now) you need to make sure you always keep a reference to the worker accessible from somewhere, otherwise when the garbage collector kicks in it may kill the worker. This is only necessary if you're using setTimeout or similar in the worker; if it just responds to messages, you won't have a problem.
Maybe worker var must be global
var worker;
$(document).ready(function () {
worker = new Worker("js/TestWorker.js");
worker.addEventListener('message', function (event) {
console.log(event.data);
});
worker.addEventListener('error', function (event) {
console.log(event);
});
});
(function () {
...
})();
This is a anonymous function which will be called once after definition and after that the browser throws it away.
Your web worker is defined it that scope and that's why it's only working for a short period of time.
I have a function that performs a long task. I would like to create a function that is able to notify the caller of the progress. Ultimately I want to update the UI with the current progress.
Something like this:
function myLongMethod(progressCallback)
{
for(var i = 0 ... )
{
progressCallback(i) ;
}
}
This works but updates on UI are not smooth. Is there a better way? I would prefer something with a jquery Deferred object using deferred.notify(). Any ideas?
Your code is fine. You have got another problem. Javscript always runs on the UI thread. Your operation is blocking this thread (the browser) and you will see some blocking of your browser window.
Luckily there is a workaround implemented in modern browser called web workers. It's simple just call in your main script another script which then get executed:
var w = new Worker("another_script.js");
If your worker is ready you can react on the result by adding a event listner to the worker:
w.onmessage = function(event) {
//do something
}
When you use this pattern, your UI did not block. You can even return data from a web worker and include scripts into it. More details you can find here and here is a good starting tutorial.
Hi you can apply the easing effect to your UI for smoothness and i am giving the following code it may help you
var oldProgress = 0;
var uiUpdater = null;
function updateUI(newProgress){
if(uiUpdater !=null){
// update your ui to the old progress first
window.clearInterval(uiUpdater); // clearing the previous timer
}
var diff = newProgress - oldProgress;
oldProgress = newProgress;
var stepSize = diff/5; // applying the new change in 5 steps to the UI
uiUpdater = window.setInterVal(function(){
// updating your UI after every 100 milliseconds
// to give the smoothness
diff -= stepSize; // decreasing the difference gradually
if(diff<=0){
window.clearInterval(uiUpdater); // clearing the interval once the update is done
}
},100);
}
You have to call the "updateUI" method from you callback with the new progress.
I'm trying to have my backbone application check the server as often as possible for updates to a model, similar to how twitter's site has new tweets that are automatically added.
My current setup is checking an external application through their api so I have no access to their server which leaves me to rely on the client side to do the checking without being too memory hungry, how can I achieve this?
In Javascript the only way you can really control timing is through setTimeout/setInterval; there is no "more sophisticated" mechanism, unless you count helper functions (eg. 'delay') which just wrap setTimeout/setInterval.
So, dmi3y's answer was correct. However, since you mentioned Backbone in both the tags and in the description, here's a more Backbone-ish version...
var YourModelClass = Backbone.Model.extend({url: remoteUrl});
var instance = new YourModelClass();
var seconds = 5;
window.setInterval(_.bind(instance.fetch, instance), 1000 * seconds);
or, if you wanted to build it in to your class ...
var YourModelClass = Backbone.Model.extend({
url: remoteUrl,
initialize: function() {
var seconds = 5;
window.setInterval(_.bind(this.fetch, this), 1000 * seconds);
}
});
var instance = new YourModelClass();
It's also worth mentioning that setInterval returns an object which you can pass to clearInterval if you want to stop "polling".
P.S. Just in case you're not familiar with _.bind, it comes from the Underscore library, which Backbone depends on so you already have it. All it does is fix this in place, so that when your timeout/interval function resolves, the this inside it will be the second argument to _.bind (and not window, which is what it would normally be).
possible solution
(function IcallTheShoots(){
console.log('am I?'); // any way you able communicate with server
window.setTimeout(IcallTheShoots, 1500);
})();
why setTimeout instead of setInterval, cause it makes sure next cycle will be called only when current is finished