While doing connect through the Nodejs api to the Solace message persistence queue, we encounter error:
{"message":"Consumer is not supported by router for this client","name":"OperationError","subcode":3,"reason":null}
on using the Solace Node.js API sample to connected to the Persistence Message Queue with the Web Message URI using soladmin.
consumer.connect = function (argv) {
if (consumer.session !== null) {
consumer.log('Already connected and ready to consume messages.');
return;
}
// extract params
//if (argv.length < (2 + 3)) { // expecting 3 real arguments
if (argv.length < 4) {
consumer.log('Cannot connect: expecting all arguments' +
'[<protocol://host[:port]>, <client-username>, <message-vpn>, <client-password>].\n' +
'Available protocols are ws://, wss://, http://, https://');
process.exit();
}
var hosturl = argv[0] //argv.slice(2)[0];
consumer.log('Connecting to Solace message router using url: ' + hosturl);
//var usernamevpn = argv.slice(3)[0];
var username = argv[1]//usernamevpn.split('#')[0];
consumer.log('Client username: ' + username);
var vpn = argv[2]//usernamevpn.split('#')[1];
consumer.log('Solace message router VPN name: ' + vpn);
var pass = argv[3]//argv.slice(4)[0];
// create session
try {
consumer.session = solace.SolclientFactory.createSession({
// solace.SessionProperties
url: hosturl,
vpnName: vpn,
userName: username,
password: pass,
});
} catch (error) {
consumer.log(error.toString());
}
The message "Consumer is not supported by router for this client" indicates that the issue is on the remote end.
Check if the client-profile allows the client to receive guaranteed messages. Otherwise, there may be a problem with the message spool that prevents the message broker from providing guaranteed messaging service.
The code snippet in the question is for initiating a connection only, and has nothing to do with connecting to the queue. The operation that failed should be
consumer.messageConsumer.connect();
Related
I am using the ws module to implement a WebSocket server in NodeJS. On the client-side, I request the connection using
webSocket = new WebSocket(url);
On the server-side, I have code that handles the 'connect' event, in which I print out the number of clients using
console.log("Total number of clients = " + wsServer.clients.size);
If I open the client-side in different tabs (or browsers), the number of clients is incremented for each new connection (as expected).
If I refresh a page, the webSocket = new WebSocket(url); code is called again and on the server the code handling the 'connect' event (see below) is also called again. However, in this case, the number of clients is not incremented. This is nice behaviour as it maintains the number of connections one wants, but I cannot see how this is done. I want to be able to test if this is an existing connection as I have a chat room running that says 'so-and-so' has joined when a new connection is made. However, I don't want this to happen every time a user refreshes their page.
Here is the server-side event-handler:
// On connection
wsServer.on("connection", function (ws, req) {
let { query } = url.parse(req.url, true);
ws.userName = ("name" in query) ? query.name : null;
ws.roomCode = ("roomCode" in query) ? query.roomCode : null;
ws.userPIN = ("PIN" in query) ? query.PIN : null;
console.log(ws.userName + " joined room " + ws.roomCode);
console.log("Total number of clients = " + wsServer.clients.size);
let data = {
userName: "Server",
message: ws.userName + " joined the room."
};
let dataStr = JSON.stringify(data);
// Loop through each client
wsServer.clients.forEach((client) => {
// Check if client is ready and in same room
if ((client.readyState === WebSocket.OPEN) && (client.roomCode == ws.roomCode)) {
client.send(dataStr);
}
});
// On message event
ws.on("message", function (msg) {
// Loop through each client
wsServer.clients.forEach((client) => {
// Check if client is ready and in same room
if ((client.readyState === WebSocket.OPEN) && (client.roomCode == ws.roomCode)) {
client.send(msg.toString());
}
});
});
});
As one can see, I do modify the client by adding name and room code fields to it but these are not present when the 'connect' event fires, implying the object is being created from scratch. However, no extra client is being added to the clients list, so what I would like to understand is:
how does the ws package know this is an existing connection?
how can I test for this?
Any advice would be gratefully received!
On investigation, it turns out the when the user refreshes the page, the WebSocket connection is closed and then re-opened. Hence there is no testing for an existing client, the existing client is just deleted on the server and the new connection added - hence the number of clients does not appear to change.
I have followed all the instruction for setting up a "Downstream Device" to send messages through IoT Edge running in Transparent Gateway. I believe my routing rules are correct, but my Function module is not receiving any of the Messages through the message flow.
These are the instruction I've followed:
https://learn.microsoft.com/en-us/azure/iot-edge/how-to-create-transparent-gateway-linux
I am using 2 Linxu VMs (ubuntu 16.04.5).
IoT Edge Transparent Gateway VM is configured with all the certs properly setup, configured and verified. I've been able to using the openssl tool from the
openssl s_client -connect {my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com:8883 -CAfile /certs/certs/azure-iot-test-only.root.ca.cert.pem -showcerts
Downstream device running on Linux VM with Certs installed and verified. My connection string is as follows:
HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key}=GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
a. I have verified I get a successful verification of the SSL cert using the openssl tool.
b. I'm using the the following in my downstream device for my connection using the NodeJS SDK
var client = DeviceClient.fromConnectionString(connectionString, Mqtt);
c. I can see the messages showing up at the Azure IoT Hub in the Cloud, but I can't get my module running on the IoT Edge Transparent Gateway to be hit.
Here are my routing rules configured for the edgeHub as specified in "Routing messages from downstream devices" in the sample doc page.
This is what the example docs show:
{ "routes":{ "sensorToAIInsightsInput1":"FROM /messages/* WHERE NOT IS_DEFINED($connectionModuleId) INTO BrokeredEndpoint(\"/modules/ai_insights/inputs/input1\")", "AIInsightsToIoTHub":"FROM /messages/modules/ai_insights/outputs/output1 INTO $upstream" } }
This is what my routing configuration is set to:
"routes": {
"downstreamBatterySensorToBatteryDataFunctionInput1": "FROM /* WHERE NOT IS_DEFINED($connectionModuleId) INTO BrokeredEndpoint(\"/modules/BatteryDataFunctionModule/inputs/input1\")",
"BatteryDataFunctionModuleToIoTHub": "FROM /messages/modules/BatteryDataFunctionModule/outputs/* INTO $upstream"
}
** Note that I've used by "FROM /* WHERE NOT IS_DEFINED" and "FROM /messages/* WHERE NOT IS_DEFINED"
My module on the IoT Edge is setup as a Function. When I use the out of the box example where the simulator device is another module running on the IoT Edge, then my function is hit correctly. Its only when I'm trying to use a "Downstream Device" that the module is not being triggered.
I have enabled "Debug Logging for the IoT Edge Service" running on my Transparent Gateway.
This is the basic Run method for the Function module:
#r "Microsoft.Azure.Devices.Client"
#r "Newtonsoft.Json"
using System.IO;
using Microsoft.Azure.Devices.Client;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
// Filter messages based on the temperature value in the body of the message and the temperature threshold value.
public static async Task Run(Message messageReceived, IAsyncCollector<Message> output, TraceWriter log)
{
How can I figure out how to get my Module running in IoT Edge to be hit/triggered from a Downstream device?
So, you say you are seeing messages show up in IoT Hub, but not in Edge... A couple of things:
you posted this as your connection string in your node app:
HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key}=GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
Did you copy/paste this exactly? the reason I ask is that, between the shared access key and the word "GatewayHostName", you have an equals sign and not a semi-colon..
it should be:
HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key};GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
(note the ';' before GatewayHostName… if you really did have an equals sign there instead of a semicolon, there's no telling what kind of chaos that would cause :-)
Secondly, in your route, you call your module BatteryDataFunctionModule.. just want to make sure that module name is exact, including being case-sensitive. You probably know that, but don't want to assume..
Finally, if the two things above check out, can you add an addition debugging route that sends the 'incoming data' to IoTHub as well..
"FROM /* WHERE NOT IS_DEFINED($connectionModuleId) INTO $upstream"
so we can make sure the messages are actually making it through iot edge.
There are 2 problems that needed to be addressed to get the Downstream Device to communication
Thanks to #Steve-Busby-Msft I needed to have a semi-colon (;) at the end of the SharedAccessKey and before the GatewayHostName
you posted this as your connection string in your node app: HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key}=GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
The NodeJS application Downstream Device also has to load up the cert correctly at the 'Application level'.
Notice the section of code for
var edge_ca_cert_path = '[Path to Edge CA certificate]';
Node JS Downstream Application
'use strict';
var fs = require('fs');
var Protocol = require('azure-iot-device-mqtt').Mqtt;
// Uncomment one of these transports and then change it in fromConnectionString to test other transports
// var Protocol = require('azure-iot-device-http').Http;
// var Protocol = require('azure-iot-device-amqp').Amqp;
var Client = require('azure-iot-device').Client;
var Message = require('azure-iot-device').Message;
// 1) Obtain the connection string for your downstream device and to it
// append this string GatewayHostName=<edge device hostname>;
// 2) The edge device hostname is the hostname set in the config.yaml of the Edge device
// to which this sample will connect to.
//
// The resulting string should look like the following
// "HostName=<iothub_host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>;GatewayHostName=<edge device hostname>"
var connectionString = '[Downstream device IoT Edge connection string]';
// Path to the Edge "owner" root CA certificate
var edge_ca_cert_path = '[Path to Edge CA certificate]';
// fromConnectionString must specify a transport constructor, coming from any transport package.
var client = Client.fromConnectionString(connectionString, Protocol);
var connectCallback = function (err) {
if (err) {
console.error('Could not connect: ' + err.message);
} else {
console.log('Client connected');
client.on('message', function (msg) {
console.log('Id: ' + msg.messageId + ' Body: ' + msg.data);
// When using MQTT the following line is a no-op.
client.complete(msg, printResultFor('completed'));
// The AMQP and HTTP transports also have the notion of completing, rejecting or abandoning the message.
// When completing a message, the service that sent the C2D message is notified that the message has been processed.
// When rejecting a message, the service that sent the C2D message is notified that the message won't be processed by the device. the method to use is client.reject(msg, callback).
// When abandoning the message, IoT Hub will immediately try to resend it. The method to use is client.abandon(msg, callback).
// MQTT is simpler: it accepts the message by default, and doesn't support rejecting or abandoning a message.
});
// Create a message and send it to the IoT Hub every second
var sendInterval = setInterval(function () {
var windSpeed = 10 + (Math.random() * 4); // range: [10, 14]
var temperature = 20 + (Math.random() * 10); // range: [20, 30]
var humidity = 60 + (Math.random() * 20); // range: [60, 80]
var data = JSON.stringify({ deviceId: 'myFirstDownstreamDevice', windSpeed: windSpeed, temperature: temperature, humidity: humidity });
var message = new Message(data);
message.properties.add('temperatureAlert', (temperature > 28) ? 'true' : 'false');
console.log('Sending message: ' + message.getData());
client.sendEvent(message, printResultFor('send'));
}, 2000);
client.on('error', function (err) {
console.error(err.message);
});
client.on('disconnect', function () {
clearInterval(sendInterval);
client.removeAllListeners();
client.open(connectCallback);
});
}
};
// Provide the Azure IoT device client via setOptions with the X509
// Edge root CA certificate that was used to setup the Edge runtime
var options = {
ca : fs.readFileSync(edge_ca_cert_path, 'utf-8'),
};
client.setOptions(options, function(err) {
if (err) {
console.log('SetOptions Error: ' + err);
} else {
client.open(connectCallback);
}
});
Is there any javascript sdk or library available to connect to Azure IoT or Event hub from Browser side?
I would like to avoid the latency involved in the redirection of the messages from Web application to browser in connecting to the Event hub and instead achieve it directly from browser.
This question discusses an approach using AMQP over Websockets to connect to the IoT/Event hub but the links are broken. In general, What are the options or approaches available for reliable real-time monitoring of data on the browser?
npm install mqtt crypto-js --save
index.js
import mqtt from 'mqtt';
import CryptoJS from 'crypto-js';
var host='{iothubname}.azure-devices.net';
var deviceId = '{DeviceId}';
var sharedKey = '{DeviceKey}';
var topic ='devices/'+deviceId+'/messages/devicebound/#';
function encodeUriComponentStrict (str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16);
});
}
function getSaSToken (hostName,deviceId,sharedKey){
var sr = encodeUriComponentStrict(hostName + '/devices/' + deviceId);
var se = Math.round(new Date().getTime() / 1000) + 24 * 3600;
var StringToSign = sr + '\n' + se;
var sig = encodeUriComponentStrict(CryptoJS.HmacSHA256(StringToSign, CryptoJS.enc.Base64.parse(sharedKey)).toString(CryptoJS.enc.Base64));
return 'SharedAccessSignature sr=' + sr + '&sig=' + sig + '&se=' + se;
}
var client = mqtt.connect({
host:host,
port:443,
path:'/$iothub/websocket?iothub-no-client-cert=true',
protocol: 'mqtts',
protocolId: 'MQTT',
protocolVersion: 4,
clientId:deviceId,
username: host+'/'+deviceId+'/api-version=2016-11-14',
password: getSaSToken(host,deviceId,sharedKey),
keepalive: 30000
})
client.on('connect',function(packet){
console.log('mqtt connected!',packet);
client.subscribe(topic);
})
client.on('reconnect',function(){
console.log('mqtt reconnected!');
})
client.on('close',function(c){
console.log('mqtt closed!',c);
})
client.on('message',function(topic, message, packet){
var string = new TextDecoder("utf-8").decode(message);
console.log('receive!',string);
})
How to get device id and device key:
Login azure
All resources and find your iot hub
Device Explorer
Click the device which you want to connect or create one
Send a test message:
Click "Send Message"
Type something and send
You will see "receive! this is a test!" on your browser console
i wrote a simple chat room demo with grpc lib, but only one client can connect to the server at one time, weird. Here is my code, can anyone give my some advise on it?
full code Link from Github
client:
const chat_proto = grpc.load(__dirname + '/chat.proto').demo;
const client = new chat_proto.Chat('localhost:50051');
server:
let chatServer = getService();
chatServer.bind('localhost:50051');
chatServer.listen();
chat.proto:
syntax = "proto3";
package demo;
service Chat {
rpc sayHi(userName) returns (welMessage) {}
rpc ChatWith(stream chatNote) returns (stream chatNote) {}
}
message userName {
string name = 1;
}
message welMessage {
string message = 1;
}
message chatNote {
string message = 1;
}
Error:
client return Error at /Users/chopper/WebstormProjects/grpcexamples/node_modules/grpc/src/client.js:461:23 which means response.status.code !== grpc.status.OK
server return Segmentation fault: 11
I'm using the node-xmpp module to connect to a XMPP server and join a group chat. Connecting to the server, setting the presence, joining the room and reading out messages works so far. But I want to receive the userlist of the room too.
The XMPP protocol requires to send a presence stanza when the client enters the room (http://xmpp.org/extensions/xep-0045.html#enter-pres). But how can I now parse it in node?
My code currently looks like this:
var xmpp = require('node-xmpp');
// Create the XMPP Client
var cl = new xmpp.Client({
jid: jid,
password: password,
reconnect: true
});
// Do things when online
cl.on('online', function() {
util.log("We're online!");
// Set client's presence
cl.send(new xmpp.Element('presence', { type: 'available' }).c('show').t('chat'));
cl.send(new xmpp.Element('presence', { to: room_jid+'/'+room_nick }).c('x', { xmlns: 'http://jabber.org/protocol/muc' }).c('history', {seconds: 1}));
// Send keepalive
setInterval(function() {
cl.send(' ');
}, 30000);
cl.on('stanza', function(stanza) {
// always log error stanzas
if (stanza.attrs.type == 'error') {
util.log('[error] ' + stanza);
return;
}
// ignore everything that isn't a room message
if (!stanza.is('message') || !stanza.attrs.type == 'chat') {
return;
}
var body = stanza.getChild('body');
// message without body is probably a topic change
if (!body) {
return;
}
// Extract username
var from, room, _ref;
_ref = stanza.attrs.from.split('/'), room = _ref[0], from = _ref[1];
var message = body.getText();
// Log topics and messages to the console
if(!from) {
util.log('Topic: ' + message);
} else {
util.log('[' + from + ']: ' + message);
}
});
});
I already tried triggering presence by using
if(stanza.is('presence')) {}
within the cl.on('stanza') part but it doesn't work.
UPDATE: I'm describing a new method now which doesn't require the client to send requests.
Background: When the client joins a group chat, the server returns presence stanzas which contain information about the connected users to the group chat.
cl.on('stanza', function(stanza) {
// always log error stanzas
if (stanza.attrs.type == 'error') {
util.log('[error] ' + stanza);
return;
}
if(stanza.is('presence')){
// We are only interested in stanzas with <x> in the payload or it will throw some errors
if(stanza.getChild('x') !== undefined) {
// Deciding what to do based on the xmlns attribute
var _presXmlns = stanza.getChild('x').attrs.xmlns;
switch(_presXmlns) {
// If someone is joining or leaving
case 'http://jabber.org/protocol/muc#user':
// Get the role of joiner/leaver
_presRole = stanza.getChild('x').getChild('item').attrs.role;
// Get the JID of joiner/leaver
_presJID = stanza.getChild('x').getChild('item').attrs.jid;
// Get the nick of joiner/leaver
_presNick = stanza.attrs.from.split('/')[1];
// If it's not none, this user must be joining or changing his nick
if(_presRole !== 'none') {
// We are now handling the data of joinging / nick changing users. I recommend to use an in-memory store like 'dirty' [https://github.com/felixge/node-dirty] to store information of the users currentliy in the group chat.
} else {
// We are now handling the data of leaving users
}
break;
}
return;
}
return;
}
OLD METHOD
I previously described a method how to query the server for current users in the group chat. By maintaining a store where all user traffic (joining, leaving, nick changing) is stored, this is no longer required. However you could still use it to make sure the data is consistent by issues like a presence stanza was not delivered to the client correctly. That's the reason it's still described below:
To request a list with users connected to the room, you need to perform the following actions:
First send a request to the server and ask for the user list:
cl.send(new xmpp.Element('iq', {from: jid, to: room_jid, type: 'get' }).c('query', { xmlns: 'http://jabber.org/protocol/disco#items' }));
then listen for iq-stanzas, parse them and populate an array with the data:
// Catching the requested user list
if(stanza.is('iq')){
// Fetching usernames from return data (data structure: http://xmpp.org/extensions/xep-0045.html#example-12)
var _items = stanza.getChild('query').getChildren('item');
var users = new Array();
for(var i = 0; i<_items.length; i++) {
// We are building an object here to add more data later
users[i] = new Object();
users[i]['name'] = _items[i].attrs.name;
}
console.log(util.inspect(users, {depth: null, colors: true}));
return;
}
This will provide you with a user list. To request unique JIDs you have to probe every user. To keep the list up to date, you should remove users when they leave and add + probe when they join.