mirror of
https://github.com/node-red/node-red-nodes.git
synced 2025-03-01 10:37:43 +00:00
WIP
This commit is contained in:
parent
b84508b52b
commit
9b07b185aa
@ -110,9 +110,9 @@
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="stomp-server">
|
||||
<div class="form-row node-input-server">
|
||||
<label for="node-config-input-server"><i class="fa fa-bookmark"></i> Server</label>
|
||||
<input class="input-append-left" type="text" id="node-config-input-server" placeholder="localhost" style="width: 45%;" >
|
||||
<div class="form-row node-input-address">
|
||||
<label for="node-config-input-address"><i class="fa fa-bookmark"></i> Address</label>
|
||||
<input class="input-append-left" type="text" id="node-config-input-address" placeholder="localhost" style="width: 45%;" >
|
||||
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
|
||||
<input type="text" id="node-config-input-port" placeholder="Port" style="width:45px">
|
||||
</div>
|
||||
@ -126,15 +126,15 @@
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-protocolversion"><i class="fa fa-tag"></i> Protocol Version</label>
|
||||
<select type="text" id="node-config-input-protocolversion" style="display: inline-block; width: 250px; vertical-align: top;">
|
||||
<label for="node-config-input-protocolVersion"><i class="fa fa-tag"></i> Protocol Version</label>
|
||||
<select type="text" id="node-config-input-protocolVersion" style="display: inline-block; width: 250px; vertical-align: top;">
|
||||
<option value="1.0">v1.0</option>
|
||||
<option value="1.1">v1.1</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-ack"><i class="fa fa-check"></i> Enable client acknowledgement</label>
|
||||
<input type="checkbox" id="node-config-input-ack" />
|
||||
<label for="node-config-input-clientAcknowledgement"><i class="fa fa-check"></i> Enable client acknowledgement</label>
|
||||
<input type="checkbox" id="node-config-input-clientAcknowledgement" />
|
||||
</div>
|
||||
<div class="form-tips">
|
||||
Enabling the ACK (acknowledgement) will set the <code>ack</code> header to <code>client</code> while subscribing to topics.
|
||||
@ -145,12 +145,12 @@
|
||||
<input type="text" id="node-config-input-vhost" placeholder="Default is null" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-reconnectretries"><i class="fa fa-repeat"></i> Reconnect Retries</label>
|
||||
<input type="number" id="node-config-input-reconnectretries" />
|
||||
<label for="node-config-input-reconnectRetries"><i class="fa fa-repeat"></i> Reconnect Retries</label>
|
||||
<input type="number" id="node-config-input-reconnectRetries" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-reconnectdelay"><i class="fa fa-clock-o"></i> Reconnect Delay (S)</label>
|
||||
<input type="number" id="node-config-input-reconnectdelay" />
|
||||
<label for="node-config-input-reconnectDelay"><i class="fa fa-clock-o"></i> Reconnect Delay (S)</label>
|
||||
<input type="number" id="node-config-input-reconnectDelay" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
|
||||
@ -162,16 +162,14 @@
|
||||
RED.nodes.registerType('stomp-server',{
|
||||
category: 'config',
|
||||
defaults: {
|
||||
server: {required:true},
|
||||
address: {required:true},
|
||||
port: {value:61613,required:true,validate:RED.validators.number()},
|
||||
protocolversion: {value:"1.0",required:true},
|
||||
ack: {value: false},
|
||||
protocolVersion: {value:"1.0",required:true},
|
||||
clientAcknowledgement: {value: false},
|
||||
vhost: {},
|
||||
reconnectretries: {value:"0",required:true,validate:RED.validators.number()},
|
||||
reconnectdelay: {value:"0.5",required:true,validate:RED.validators.number()},
|
||||
name: {},
|
||||
clientConnection: {value: null},
|
||||
connected: {value: false}
|
||||
reconnectRetries: {value:0,required:true,validate:RED.validators.number()},
|
||||
reconnectDelay: {value:1,required:true,validate:RED.validators.number()},
|
||||
name: {}
|
||||
},
|
||||
credentials: {
|
||||
user: {type:"text"},
|
||||
|
@ -4,20 +4,245 @@ module.exports = function(RED) {
|
||||
var StompClient = require('stomp-client');
|
||||
var querystring = require('querystring');
|
||||
|
||||
// ----------------------------------------------
|
||||
// ------------------- State --------------------
|
||||
// ----------------------------------------------
|
||||
function updateStatus(node, allNodes) {
|
||||
let setStatus = setStatusDisconnected;
|
||||
|
||||
if (node.connecting) {
|
||||
setStatus = setStatusConnecting;
|
||||
} else if (node.connected) {
|
||||
setStatus = setStatusConnected;
|
||||
}
|
||||
|
||||
setStatus(node, allNodes);
|
||||
}
|
||||
|
||||
function setStatusDisconnected(node, allNodes) {
|
||||
if (allNodes) {
|
||||
for (let id in node.users) {
|
||||
if (hasProperty(node.users, id)) {
|
||||
node.users[id].status({ fill: "red", shape: "ring", text: "node-red:common.status.disconnected"});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.status({ fill: "red", shape: "ring", text: "node-red:common.status.disconnected" })
|
||||
}
|
||||
}
|
||||
|
||||
function setStatusConnecting(node, allNodes) {
|
||||
if(allNodes) {
|
||||
for (var id in node.users) {
|
||||
if (hasProperty(node.users, id)) {
|
||||
node.users[id].status({ fill: "yellow", shape: "ring", text: "node-red:common.status.connecting" });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.status({ fill: "yellow", shape: "ring", text: "node-red:common.status.connecting" });
|
||||
}
|
||||
}
|
||||
|
||||
function setStatusConnected(node, allNodes) {
|
||||
if(allNodes) {
|
||||
for (var id in node.users) {
|
||||
if (hasProperty(node.users, id)) {
|
||||
node.users[id].status({ fill: "green", shape: "dot", text: "node-red:common.status.connected" });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.status({ fill: "green", shape: "dot", text: "node-red:common.status.connected" });
|
||||
}
|
||||
}
|
||||
|
||||
function setStatusError(node, allNodes) {
|
||||
if(allNodes) {
|
||||
for (var id in node.users) {
|
||||
if (hasProperty(node.users, id)) {
|
||||
node.users[id].status({ fill: "red", shape: "dot", text: "error" });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.status({ fill: "red", shape: "dot", text: "error" });
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
// ---------------- Connection ------------------
|
||||
// ----------------------------------------------
|
||||
function handleConnectAction(node, msg, done) {
|
||||
const clientOptions = typeof msg.clientOptions === 'object' ? msg.clientOptions : null;
|
||||
if (clientOptions) {
|
||||
if (!node.client) {
|
||||
// Client has not been initialized - initialize client and connect
|
||||
node.client = new StompClient(clientOptions);
|
||||
node.client.connect(function(sessionId) {
|
||||
done(sessionId);
|
||||
});
|
||||
} else {
|
||||
// Already connected/connecting/reconnecting
|
||||
if (clientOptions.forceReconnect) {
|
||||
// The force flag tells us to cycle the connection
|
||||
node.client.disconnect(function() {
|
||||
node.client = new StompClient(clientOptions);
|
||||
node.client.connect(function(sessionId) {
|
||||
done(sessionId)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
done(new Error("No connection options provided"));
|
||||
}
|
||||
}
|
||||
|
||||
function handleDisconnectAction(node, done) {
|
||||
node.client.disconnect(function() {
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
function StompServerNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.server = n.server;
|
||||
this.port = n.port;
|
||||
this.protocolversion = n.protocolversion;
|
||||
this.ack = n.ack;
|
||||
this.vhost = n.vhost;
|
||||
this.reconnectretries = n.reconnectretries || 999999;
|
||||
this.reconnectdelay = (n.reconnectdelay || 15) * 1000;
|
||||
this.name = n.name;
|
||||
this.clientConnection = n.clientConnection;
|
||||
this.connected = n.connected;
|
||||
this.username = this.credentials.user;
|
||||
this.password = this.credentials.password;
|
||||
const node = this;
|
||||
// To keep track of processing nodes that use this config node for their connection
|
||||
node.users = {};
|
||||
// Config node state
|
||||
node.connected = false;
|
||||
node.connecting = false;
|
||||
node.closing = false;
|
||||
node.options = {};
|
||||
// node.subscriptions = {};
|
||||
node.sessionId = null;
|
||||
/** @type {StompClient} */
|
||||
node.client;
|
||||
node.setOptions = function(options, init) {
|
||||
if (!options || typeof options !== "object") {
|
||||
return; // Nothing to change
|
||||
}
|
||||
|
||||
// Apply property changes (only if the property exists in the options object)
|
||||
setIfHasProperty(options, node, "address", init);
|
||||
setIfHasProperty(options, node, "port", init);
|
||||
setIfHasProperty(options, node, "protocolVersion", init);
|
||||
setIfHasProperty(options, node, "clientAcknowledgement", init);
|
||||
setIfHasProperty(options, node, "vhost", init);
|
||||
setIfHasProperty(options, node, "reconnectRetries", init);
|
||||
setIfHasProperty(options, node, "reconnectDelay", init);
|
||||
|
||||
if (node.credentials) {
|
||||
node.username = node.credentials.username;
|
||||
node.password = node.credentials.password;
|
||||
}
|
||||
if (!init && hasProperty(options, "username")) {
|
||||
node.username = options.username;
|
||||
}
|
||||
if (!init && hasProperty(options, "password")) {
|
||||
node.password = options.password;
|
||||
}
|
||||
|
||||
// Build options for passing to the stomp-client API
|
||||
node.options = {
|
||||
address: node.address,
|
||||
port: node.port * 1,
|
||||
user: node.username,
|
||||
pass: node.password,
|
||||
protocolVersion: node.protocolVersion,
|
||||
reconnectOpts: {
|
||||
retries: node.reconnectRetries * 1,
|
||||
delay: node.reconnectDelay * 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
node.setOptions(n, true);
|
||||
|
||||
// Define functions called by STOMP processing nodes
|
||||
node.register = function(stompNode) {
|
||||
node.users[stompNode.id] = stompNode;
|
||||
// Auto connect when first STOMP processing node is added
|
||||
if (Object.keys(node.users).length === 1) {
|
||||
node.connect();
|
||||
// Update nodes status
|
||||
setTimeout(function() { updateStatus(node, true) }, 1);
|
||||
}
|
||||
}
|
||||
|
||||
node.deregister = function(stompNode, done, autoDisconnect) {
|
||||
delete node.users[stompNode.id];
|
||||
if (autoDisconnect && !node.closing && node.connected && Object.keys(node.users).length === 0) {
|
||||
node.disconnect(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
node.canConnect = function() {
|
||||
return !node.connected && !node.connecting;
|
||||
}
|
||||
|
||||
node.connect = function(callback) {
|
||||
if (node.canConnect()) {
|
||||
node.closing = false;
|
||||
node.connecting = true;
|
||||
setStatusConnecting(node, true);
|
||||
|
||||
try {
|
||||
// Remove left over client if needed
|
||||
if (node.client) {
|
||||
node.client.disconnect();
|
||||
node.client = null;
|
||||
}
|
||||
|
||||
node.client = new StompClient(node.options);
|
||||
node.client.connect(function(sessionId) {
|
||||
node.sessionId = sessionId;
|
||||
});
|
||||
|
||||
node.client.on("connect", function() {
|
||||
node.closing = false;
|
||||
node.connecting = false;
|
||||
node.connected = true;
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
|
||||
node.log("Connected to STOMP server", {sessionId: node.sessionId, url: `${node.options.address}:${node.options.port}`, protocolVersion: node.options.protocolVersion});
|
||||
setStatusConnected(node, true);
|
||||
});
|
||||
|
||||
node.client.on("reconnect", function(sessionId, numOfRetries) {
|
||||
node.connecting = false;
|
||||
node.connected = true;
|
||||
node.sessionId = sessionId;
|
||||
|
||||
node.log("Reconnected to STOMP server", {sessionId: node.sessionId, url: `${node.options.address}:${node.options.port}`, protocolVersion: node.options.protocolVersion, retries: numOfRetries});
|
||||
setStatusConnected(node, true);
|
||||
});
|
||||
|
||||
node.client.on("reconnecting", function() {
|
||||
node.warn("reconnecting");
|
||||
node.connecting = true;
|
||||
node.connected = false;
|
||||
|
||||
node.log("Reconnecting to STOMP server...", {url: `${node.options.address}:${node.options.port}`, protocolVersion: node.options.protocolVersion});
|
||||
setStatusConnecting(node, true);
|
||||
});
|
||||
|
||||
node.client.on("error", function(err) {
|
||||
node.error(err);
|
||||
setStatusError(node, true);
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
node.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.disconnect = function(callback) {
|
||||
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("stomp-server",StompServerNode,{
|
||||
credentials: {
|
||||
@ -283,3 +508,36 @@ module.exports = function(RED) {
|
||||
RED.nodes.registerType("stomp ack",StompAckNode);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------
|
||||
// ----------------- Helpers --------------------
|
||||
// ----------------------------------------------
|
||||
/**
|
||||
* Helper function for applying changes to an objects properties ONLY when the src object actually has the property.
|
||||
* This avoids setting a `dst` property null/undefined when the `src` object doesnt have the named property.
|
||||
* @param {object} src Source object containing properties
|
||||
* @param {object} dst Destination object to set property
|
||||
* @param {string} propName The property name to set in the Destination object
|
||||
* @param {boolean} force force the dst property to be updated/created even if src property is empty
|
||||
*/
|
||||
function setIfHasProperty(src, dst, propName, force) {
|
||||
if (src && dst && propName) {
|
||||
const ok = force || hasProperty(src, propName);
|
||||
if (ok) {
|
||||
dst[propName] = src[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to test an object has a property
|
||||
* @param {object} obj Object to test
|
||||
* @param {string} propName Name of property to find
|
||||
* @returns true if object has property `propName`
|
||||
*/
|
||||
function hasProperty(obj, propName) {
|
||||
//JavaScript does not protect the property name hasOwnProperty
|
||||
//Object.prototype.hasOwnProperty.call is the recommended/safer test
|
||||
return Object.prototype.hasOwnProperty.call(obj, propName);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user