Using domain with serialport-node - node-serialport

I have the node-serialport library working fine in a happy-path situation, but I'd like to address error handling.
The particular test case I'm addressing at the moment is simply that the serial device isn't plugged in. Ideally, I'd be able to detect that a problem exists, and then just retry after a short delay.
The problem is that I keep getting the following error:
events.js:72
throw er; // Unhandled 'error' event
Error: cannot open /dev/ttyACM0
I would expect something like this, since the device isn't plugged in, but I need to catch it. try/catch obviously doesn't work, since it's asynchronous. So I am trying "domain", which appears to be the recommended way:
function reconnect() {
var d = domain.create();
d.on("error", function(err) {
console.error(err);
setTimeout(reconnect, RETRY_DELAY_MS);
});
d.run(function() {
var gps = new SerialPort("/dev/ttyACM0");
gps.on("open", function() {
console.log("Success!");
});
gps.on("error", function(err) {
console.error(err);
setTimeout(reconnect, RETRY_DELAY_MS);
});
});
}
I would expect this to have the desired effect, but I get the same error as above.
Does anyone see anything that I'm just not getting?

The trick is to provide a callback when instantiating the SerialPort object
var serial = new serialPort("/dev/ttyUSB0", { baudrate : 115200 },
function(error) {
if(error)
{
console.log("INIT ERROR: " + error.message + "\n");
}
});

Related

Node local server stops instantly as the code does throw err;

So, in this piece of code I'm trying to use findOne to find and delete a particular dishId from my Favorites document. The code is working fine if I send a valid dishId but when I enter a wrong dishId the code does throw err; and the Node server stops with this error.
events.js:292
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'dishes' of null
And then I've to do npm start again. So how should I tackle this? I don't want server to stop. I want it to keep going so that I can do further requests. Here's my code.
favoriteRouter.route('/:dishId')
.delete(cors.corsWithOptions, authenticate.verifyUser, (req,res,next) => {
Favorites.findOne({user: req.user._id, dishes: req.params.dishId} ,(err, favdel) => {
if(err) {
throw err;
}
else {
favdel.dishes.pull({_id:req.params.dishId});
favdel.save();
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(favdel);
}
})
});
The error is pretty clear: you're blindly accessing a property without first checking whether it exists, so: check if favdel.dishes exists before you try to get data out of it. And if it doesn't, make error handling kick in, in a way that makes sure to send the correct HTTP error code.
...
if (!favdel || !favdel.dishes) {
// only you know which 4xx or even 5xx error this should be
return next(new Error("..."));
}
favdel.dishes.pull({_id:req.params.dishId});
favdel.save();
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(favdel);
...

Catch fails on connection to Mongo

the answer to this question: How to get node to exit when mongo connect fails contains async/wait code for a connection
however, my code (running on node v11.5.0 and mongodb v3.1.13) is failing to catch:
(async function() {
let db;
try {
db = await MongoClient.connect(uri, { useNewUrlParser: true });
console.log("RETURN", db);
} catch (err) {
console.log('EXITING');
process.exit(1);
}
}());
to prove the point I intentionally give a uri without credentials:
mongodb://undefined#cluster0-shard-00-00-z4j9e.azure.mongodb.net:27017,cluster0-shard-00-01-z4j9e.azure.mongodb.net:27017,cluster0-shard-00-02-z4j9e.azure.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin&retryWrites=true
and what I get is output like this:
/Users/ekkis/dev/mongo/node_modules/mongodb/lib/topologies/replset.js:346
throw err;
^
MongoError: password must be a string
at passwordDigest (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/auth/scram.js:63:43)
at ScramSHA1.ScramSHA.auth (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/auth/scram.js:175:25)
at authenticate (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/connection/pool.js:232:17)
at authenticateLiveConnections (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/connection/pool.js:819:7)
at /Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/connection/pool.js:864:5
at waitForLogout (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/connection/pool.js:855:34)
at Pool.auth (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/connection/pool.js:862:3)
at Server.auth (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/topologies/server.js:931:20)
at auth (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/topologies/replset.js:1474:19)
at ReplSet.auth (/Users/ekkis/dev/mongo/node_modules/mongodb-core/lib/topologies/replset.js:1492:5)
so if the error had been caught, the console should have displayed the word 'EXITING', but does not. additionally, I contend an exception was thrown because otherwise the returned value would have been printed, which it was not
how can this be? what do I need to do to get it to work?
* Appendix I *
In fact, the promises version of this exhibits the same odd behaviour, it doesn't catch:
MongoClient
.connect(uri, { useNewUrlParser: true })
.then(dbc => {
console.log('SUCCESS');
})
.catch(err => {
console.log('EXITING');
process.exit(1);
});
and yes, I tested the callback version, which also suffers the same malady. Incidentally, passing an empty string for the uri works well. I don't get it
* Appendix II *
In fact, the problem seems to be particular to the credentials passed i.e. if I pass:
mongodb://x:y#cluster0-shard-[...]
I catch a "MongoError: authentication fail" as expected. passing:
mongodb://#cluster0-shard-[...]
interestingly returns a connection but credentials missing a ":" fail in this odd way, so:
mongodb://ekkis#cluster0-shard-[...]
fails to catch
Looks to me like it's a bug with however MongoClient is setting up its connections. You won't be able to use try & catch to handle asynchronously thrown errors within MongoClient code.
const {MongoClient} = require("mongodb");
process.on("uncaughtException", (err) => {
console.log("process err", err);
process.exit(1)
})
async function run () {
let db;
try {
// connection url will throw because password isn't provided
db = await MongoClient.connect("mongodb://myUsername:#localhost", { useNewUrlParser: true });
} catch (err) {
console.log('Exiting from thrown error', err);
process.exit(1);
}
}
run();
Here's a simplified example of what's happening -- the error will end up "uncaught" and caught by the uncaughtException handler
process.on("uncaughtException", (err) => console.log("uncaught", err));
try {
setTimeout(() => {
throw new Error("asynchronously thrown error");
})
} catch (err) {
console.log("Error will not be caught here")
}
When I was using mongo version 3.6.1, it was not an issue and i was able to handle the thrown exception using catch. But after a few days on another project this type of error occurred and was showing as the error thrown from
%project_folder%/node_modules/mongodb/lib/utils.js:668
(Don't mind about the slash in the path string.)
The mongodb version this time is 3.6.3. Upon checking the code in that file at the mentioned line I found the below piece of code. where the caught error is again being thrown.
fn(function(err, res) {
if (err != null) {
try {
callback(err);
} catch (error) {
return process.nextTick(() => {
throw error;
});
}
return;
}
callback(err, res);
});
I changed the throw error to console.error(error) and the problem got resolved. But still you need to be caught somewhere in our code where connect function is called.
I think this is because the above piece of code is checking for the presence of error and passing it to the callback function and then again throwing the same error again. I suppose it is the MongoDB driver developer community's responsibility to resolve this issue.

How to handle node.js errors properly - the standart way

For about developer errors that are made by mistake or undefined or something else that can crash the whole app:
Should I use this:
var domain = require('domain');
var d = domain.create();
d.on('error', function(err) {
console.error(err);
});
d.run(function() {
if (someom.sadjaslkd.asdasd=="yes") console.log("Yes");
// run more other things in the safe area
});
Purposely someom.sadjaslkd.asdasd is undefined.
OR should I use this:
try {
if (someom.sadjaslkd.asdasd=="yes") console.log("Yes");
// run more other things in the safe area
} catch(err) {
// handle the error safely
}
For about operation errors that are made not by mistake but due to client's faults:
Should I use this:
if (somethingWorng) res.status(400).json({status: "400", error: "Some custom error message to the user"});
OR maybe I should pass the error like this:
if (somethingWorng) throw "Some custom error message to the user";
OR maybe
if (somethingWorng) throw new Error("Some custom error message to the user");
Really there tons of ways to make this work. Is there a quick easy, readable, best practice?

NodeJs/WS: How to throw an error server side handled on client side?

I'm trying to throw and error server side when my websocket has more than 2 connections. I have this nice client-side onerror method but I'm unable to reach that part of my code. I'm using nodeJS and the package ws which has the smallest doc on error handling.
server.js
theWebSocketServer.on('connection', function connection(websocket){
if (theWebSocketServer.clients.length >2) {
// I want to throw the error here and pass it to onerror
console.log('No access allowed', theWebSocketServer.clients.length)
} else {
console.log('happy connection', theWebSocketServer.clients.length)
}
})
client.js
wsConnection.onerror = function(eventInfo) {
alert("There was a connection error!");
console.log("Socket error!", eventInfo);
}
How can I send an error to be handled on the client side JS?
In the docs, I can't find any way to send an error to the client. Since ws is a smallish module for websockets, I think it can be used to send messages between server and client and if you need fancy stuff you need to implement your own protocol (the way how you interpret those messages).
For example, in this case it could be something like this:
Client
wsConnection.onmessage = (m) => {
// You can define errors by checking if the event data contains
// something specific: such as the message starts with "Error"
// or if the property of the object is "error" and so on.
if (m.data.startsWith("Error")) {
alert(m.data)
// This will show in the popup:
// "Error: No access allowed"
} else {
// do something else
}
};
wsConnection.onerror = function(eventInfo) {
/* Handle socket errors – e.g. internet goes down, connection is broken etc. */
}
Server:
theWebSocketServer.on('connection', function connection(websocket){
if (theWebSocketServer.clients.length >2) {
websocket.send("Error: No access allowed", err => {
// Ensure your data was actually sent successfully and then
// Close the connection
websocket.close()
// Just in case your data was not sent because
// of an error, you may be interested so see what happened
if (err) { return console.error(err); }
})
// I want to throw the error here and pass it to onerror
console.log('No access allowed', theWebSocketServer.clients.length)
} else {
console.log('happy connection', theWebSocketServer.clients.length)
}
})

Node returning before callback?

Sorry if this is due to a misunderstanding of how callbacks work, but I'm new to Node and really need to return useful error messages, but at the moment return a success followed later by the error which is caught:
connectDevice: function (ip) {
var port = 5555;
console.log("Connecting to device " + ip + ":5555")
client.connect(ip, port)
.then(function() {
return false; // wait until no errors confirmed
})
.catch(function(err) {
if (err === null) {
return false;
} else {
if (debug) console.error('Could not connect to device at IP ' + ip, err.stack);
console.log('Could not connect to device');
return true; // return error = true
}
});
},
I'm trying to connect to an android device using adbkit which uses promises, however I can't figure out how to adapt them from examples as there's no parent function.
It's important it waits to confirm a connecion before sending commands however. I get this is because of Node's event driven nature but don't know enough how to properly deal with this situation.
Thanks for any help and patience.

Resources