Node-SerialPort Issue w/ Multiple Writes - node.js

When I pass an array with 1 element everything works great, when I pass one with two (max for our use case) I get the following error:
There's no write queue for that file descriptor (after write)!
Here is my code:
exports.triggerPhysical = function(state, alerts) {
console.dir("IN PHYSICAL");
console.dir(alerts);
SerialPort.list(function(err, ports) {
var port = {};
for(var i = 0; i < ports.length; i++) {
try {
if(typeof ports[i].manufacturer != 'undefined' && ports[i].manufacturer.includes("Numato")) {
port = ports[i];
}
} catch(err) {
console.dir(err);
}
}
var numato = new SerialPort(port.comName, {baudrate : 19200}, function(err) {
if(err) {
return console.dir(err);
}
console.dir('calling write');
for(var j = 0; j < alerts.length; j++) {
numato.write('relay ' + state + ' ' + alerts[j].index + '\r', function(err) {
if(err) {
console.dir('error writing');
console.dir(err);
}
console.dir('serial message written');
});
}
numato.close();
return true;
});
});
}
First write works great, second one fails. I am guessing there is an obvious solution but I am not finding it. Any insight would be much appreciated.

I ended up doing two things to resolve this issue. First I upgraded to version 5.x of the node-serialport library.
Second, I changed my code to the following:
exports.triggerPhysical = function(state, alerts) {
var port = new SerialPort('/dev/ttyACM0');
port.on("open", function() {
alerts.forEach(function(alert, idx) {
str = 'relay ' + state + ' ' + alert.index + '\r';
port.write(str, function(err, results) {
if(err) {
console.dir("err writing");
console.dir(err);
} else {
console.dir(results);
}
});
});
})
port.drain(writeDone);
function writeDone() {
console.dir("In writeDone");
port.close();
}
}
I am now able to do consecutive writes without causing any errors and the port doesn't end up in a weird locked state.

Related

Array is empty, no data after for loop added

I'm using redis and nodejs for the first time, without success. Trying to loop insert data but got an empty array.
const redis = require("redis");
const client = redis.createClient({
retry_strategy: function(options) {
if (options.error && options.error.code === "ECONNREFUSED") {
// End reconnecting on a specific error and flush all commands with
// a individual error
return new Error("The server refused the connection");
}
if (options.total_retry_time > 1000 * 60 * 60) {
// End reconnecting after a specific timeout and flush all commands
// with a individual error
return new Error("Retry time exhausted");
}
if (options.attempt > 10) {
// End reconnecting with built in error
return undefined;
}
// reconnect after
return Math.min(options.attempt * 100, 3000);
},
});
var data_value = {
id: '235235',
totalrv: 'WAIT',
product: 'productName2',
url: 'url2',
process: 'proc',
task: 'msg'
};
client.set("key0", JSON.stringify(data_value));
client.set("key1", JSON.stringify(data_value));
client.set("key2", JSON.stringify(data_value));
client.set("key3", JSON.stringify(data_value));
client.set("key4", JSON.stringify(data_value));
//client.get("key2", redis.print);
var logger_data = {
logger: []
};
client.keys('*', function (err, keys) {
if (err) return console.log(err);
for(var i = 0, len = keys.length; i < len; i++) {
var values_v = client.get(keys[i].toString(), function(err, val) {
// console.log(logger_data);
// data is exist ...
logger_data.logger.push(JSON.parse(val));
});
}
});
// empty data
console.log(logger_data);
I wrote 2 print data result, in the loop it's working ok, but end of function, there are no data in array.
you can call a function inside the callback if you want to print the logger_data with values outside the asynchronous callback, like this
function printLoggerData(){
console.log(logger_data);
}
client.keys('*', function (err, keys) {
if (err) return console.log(err);
for(var i = 0, len = keys.length; i < len; i++) {
var values_v = client.get(keys[i].toString(), function(err, val) {
// console.log(logger_data);
// data is exist ...
logger_data.logger.push(JSON.parse(val));
// calling the function here so that it contains the latest values
printLoggerData();
});
}
});
Ok, with help of CertainPerformance, it's now working in asynchronous.
Thank you very much ...
async function getMessage () {
var logger_data = {
logger: []
};
return new Promise(function(resolve, reject) {
client.keys('*', function (err, keys) {
if (err) return console.log(err);
for(var i = 0, len = keys.length; i < len; i++) {
var values_v = client.get(keys[i].toString(), function(err, val) {
logger_data.logger.push(JSON.parse(val));
resolve(logger_data);
});
}
});
});
}
async function main() {
let message = await getMessage();
console.log(message);
}
main();

How to get code to execute in order in node.js

I am trying to finish my script, but for some reason i don't know, it refuses to execute in the order i put it in.
I've tried placing a 'wait' function between the JoinRequest update function and the following code, but when run, it acts as if the function call and wait function were the other way round, countering the point of the wait().
const Roblox = require('noblox.js')
var fs = require('fs');
var joinRequests = []
...
function wait(ms) {
var d = new Date();
var d2 = null;
do { d2 = new Date(); }
while(d2-d < ms*1000);
};
...
function updateJReqs() {
Roblox.getJoinRequests(4745601).then((array) => {
var i;
var final = [];
for(i = 0; i < array.length; i++) {
final.push(array[i].username);
};
if(final === '') {
final = '-None';
};
joinRequests = final
console.log('Updated join requests.')
});
}
function check() {
setTimeout(() => {
fs.readFile('Request.txt',encoding = 'utf-8', function(err, data) {
if (err) {
check();
} else {
updateJReqs(); //for some reason this function is executed alongside the below, not before it.
// Tried putting wait(x) in here.
console.log('Request received: ' + data)
var solution = joinRequests
console.log('Fuffiling request with ' + solution)
fufillRequest(solution)
fs.unlink('Request.txt', function(err) {
if(err) throw err;
});
check();
}
});
}, 400)
}
check();
The script is supposed to wait until a file is created (accomplished), update the list of join requests (accomplished) and then create a new file with the list of join requests in(not accomplished).
if I understand your code you work with async code, you need to return a promise in updateJReqs and add a condition of leaving from the function because you have an infinite recursion
function updateJReqs() {
return new Promise(resolve => {
Roblox.getJoinRequests(4745601).then((array) => {
var i;
var final = [];
for(i = 0; i < array.length; i++) {
final.push(array[i].username);
};
if(final === '') {
final = '-None';
};
joinRequests = final
console.log('Updated join requests.')
resolve();
});
}
}
async function check() {
setTimeout(() => {
fs.readFile('Request.txt',encoding = 'utf-8', function(err, data) {
if (err) {
await check();
} else {
await updateJReqs();
// Tried putting wait(x) in here.
console.log('Request received: ' + data)
var solution = joinRequests
console.log('Fuffiling request with ' + solution)
fufillRequest(solution)
fs.unlink('Request.txt', function(err) {
if(err) throw err;
});
// you dont have an exit from your function check();
return 'Success';
}
});
}, 400)
}
check().then(res => console.log(res));

Node array of function using variables from enclosing scope

I am trying to do dns.reverse() on a list of ip using async.parallel().
The code is as follows:
var functions = [];
for (var i = 0; i < data.length; i++) {
var ip = data[i].ip;
var x = function(callback) {
dns.reverse(ip, (err, hostnames) => {
if (err) {
log.error("Error resolving hostname for [" + ip + '] ' + err);
return callback(null, err);
}
callback(null, hostnames);
});
};
functions.push(x);
}
async.parallel(functions, (err, results) => {
for(var i = 0; i < data.length; i++) {
data[i]['hostnames'] = results[i];
}
handler(null, data);
});
What is happening is dns.reverse() is getting called with the same ip (the last one in data array) for all the calls. May be I am doing something wrong. Can somebody explain what is my mistake?
The first callback is executed after the entire for loop finished, because it's async.
The value of ip will be the one in the last iteration of the loop.
You could put some console.log to realize what's really happening.
The correct way of doing it might be:
async.parallel(data.map(({ ip }) => callback => {
dns.reverse(ip, callback)
}), (err, results) => {
for (var i = 0; i < data.length; i++) {
data[i]['hostnames'] = results[i];
}
handler(null, data);
})
Create a new array of functions based on each ip.
Every function will call it's callback as dns.reverse.
Also, it might be better to return a new data array, not changing data inside the loop:
(err, results) => {
const result = data.map((data, index) => ({
...data,
hostnames: results[index]
})
handler(null, result);
})
Thanks to #apokryfos I got a hint. To get the code working I just need to use let instead of var while declaring ip.
var functions = [];
for (var i = 0; i < data.length; i++) {
let ip = data[i].ip;
var x = function(callback) {
dns.reverse(ip, (err, hostnames) => {
if (err) {
log.error("Error resolving hostname for [" + ip + '] ' + err);
return callback(null, err);
}
callback(null, hostnames);
});
};
functions.push(x);
}
async.parallel(functions, (err, results) => {
for(var i = 0; i < data.length; i++) {
data[i]['hostnames'] = results[i];
}
handler(null, data);
});
For anybody interested in understanding following might be helpful: How do JavaScript closures work?

Redis in Nodejs for loop not working properly

I have a for loop like below which isn't getting executed as expected.
var redis = require('redis');
var client = redis.createClient();
var arr = [{title:"title1"},{title:"title2"},{title:"title3"},{title:"title4"}];
for(var i =0; i<arr.length; i++){
//console.log(arr[i]);
var obj1 = arr[i];
client.get(obj1.title, function(err, response){
if(err){
console.log(err);
}
if(response){
if(i%3==0){
client.del(obj1.title);
}else{
client.incr(obj1.title);
}
}else{
client.set(obj1.title, 1);
}
});
}
The output on running the below code afterwards was
for(var i=0; i<arr.length; i++){
client.get(arr[i].title, redis.print);
}
The output:
Reply: null
Reply: null
Reply: null
Reply: null
Reply: null
Reply: null
Reply: 2
which was not what i expected, since all values except the one divisible by 3 should be atleast 1;
Please create a new function. In the new function, you can delete, increment or creating the new key.
The below code works fine for me. Please check.
var redis = require('redis');
var client = redis.createClient();
var arr = [ {
title : "title1"
}, {
title : "title2"
}, {
title : "title3"
}, {
title : "title4"
} ];
function delOrIncr(obj1, i) {
client.get(obj1.title, function(err, response) {
if (err) {
console.log(err);
}
if (response) {
if (i % 3 === 0) {
console.log('Deleting >' + obj1.title);
client.del(obj1.title);
} else {
console.log('Increment >' + obj1.title);
client.incr(obj1.title);
}
} else {
console.log('Creating new >' + obj1.title);
client.set(obj1.title, 1);
}
});
}
for (var i = 0; i < arr.length; i++) {
delOrIncr(arr[i], i);
}
Note:-
Please run the get as a separate program to check the result of the above program.
var redis = require('redis');
var client = redis.createClient();
var arr = [{title:"title1"},{title:"title2"},{title:"title3"},{title:"title4"}];
for(var i =0; i<arr.length; i++){
//console.log(arr[i]); // this is cool
var obj1 = arr[i];
client.get(obj1.title, function(err, response){
if(err){
console.log(err);
}
if(response){
if(i%3==0){
// mistake 1:
// due to async op,loop will already be over and i will be 3 here
// mistake 2:
// obj1 will be arr[3] here, not what you were expecting :D
client.del(obj1.title);
}else{
client.incr(obj1.title);
}`enter code here`
}else{
// so only the last obj gets stored.
client.set(obj1.title, 1);
}
});
}

NodeJS getting stuck in callback

Im having troubles where my node app all a sudden starts to consume a lot of CPU. Im suspecting that the function below gets stuck somehow..
Client.prototype.countActiveChatsRedis = function (userID, agentID, obj, callback) {
var count = 0;
pub.keys("widgetActive:" + userID + ":*", function(err, key) {
if(err !== null) {
console.log("Redis error..... --> " + err);
callback(count, obj);
}
if(key && key.length > 0) {
pub.mget(key, function(err, data) {
if(data) {
for(var i = 0; i < data.length;i++) {
if(data[i]) {
var arr = data[i].split(",");
if(arr[2] == agentID) {
if (Number(arr[3]) > 0) {
count++;
}
}
}
}
callback(count, obj);
}
});
} else {
callback(count, obj);
}
});
}
Any ideas what the problem could be? Any case where it could avoid sending the callback?
This function runs around 50 times per second.
It is bad practice to use KEYS in a production environment. To quote the Redis master himself:
Warning: consider KEYS as a command that should only be used in
production environments with extreme care. It may ruin performance
when it is executed against large databases. This command is intended
for debugging and special operations, such as changing your keyspace
layout. Don't use KEYS in your regular application code. If you're
looking for a way to find keys in a subset of your keyspace, consider
using SCAN or sets.
Whenever you add a key with this prefix, just add it to a set called "widgetActive" the user's id or any other data you need.
you can also use HASH if you need to save some data for each entry.
you should always return your callbacks to make sure that your function terminates correctly and returns the control to the calling context:
Client.prototype.countActiveChatsRedis = function (userID, agentID, obj, callback) {
var count = 0;
pub.keys("widgetActive:" + userID + ":*", function(err, key) {
if(err !== null) {
console.log("Redis error..... --> " + err);
return callback(count, obj);
}
if(key && key.length > 0) {
pub.mget(key, function(err, data) {
if(data) {
for(var i = 0; i < data.length;i++) {
if(data[i]) {
var arr = data[i].split(",");
if(arr[2] == agentID) {
if (Number(arr[3]) > 0) {
count++;
}
}
}
}
return callback(count, obj);
}
});
} else {
return callback(count, obj);
}
});
}
It's stucked because in the case where there is no data, no callback is being called.
pub.mget(key, function(err, data) {
if(data) {
for(var i = 0; i < data.length;i++) {
if(data[i]) {
var arr = data[i].split(",");
if(arr[2] == agentID) {
if (Number(arr[3]) > 0) {
count++;
}
}
}
}
// callback(count, obj); // <==== move this callback outside of if (data)
}
callback(count, obj); // this ensures that callback always gets called
});

Resources