Skip to content

Commit

Permalink
Added new MONITOR action to support deadband filter
Browse files Browse the repository at this point in the history
  • Loading branch information
mikakaraila committed Mar 5, 2019
1 parent 4b53c5c commit 40f0c42
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 5 deletions.
27 changes: 24 additions & 3 deletions opcua/102-opcuaclient.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
defaults: {
endpoint: {value: "", required: true, type: "OpcUa-Endpoint"},
action: {value: "read", required: true},
deadbandtype: {value: "a"},
deadbandvalue: {value: 1},
time: {value: 10},
timeUnit: {value: "s"},
certificate: {value: "n"},
Expand All @@ -46,6 +48,7 @@
try {
var inputAction = $('#node-input-action');
var inputTime = $('#node-input-timerow');
var inputDeadband = $('#node-input-deadband');
change_time_input(); // first to fit config now
inputAction.change(change_time_input);

Expand All @@ -54,12 +57,19 @@
}

function change_time_input() {
if (inputAction.val() == "subscribe" || inputAction.val() == "events") {
if (inputAction.val() == "subscribe" || inputAction.val() == "events" || inputAction.val() == "monitor") {
inputTime.show();
}
else {
inputTime.hide();
}
if (inputAction.val() == "monitor") {
inputDeadband.show();
}
else {
inputDeadband.hide();
}

}
}
});
Expand All @@ -80,7 +90,8 @@
<option value="subscribe">SUBSCRIBE</option>
<option value="unsubscribe">UNSUBSCRIBE</option>
<option value="events">EVENTS</option>
<option value="info">INFO</option>
<option value="info">INFO</option>
<option value="monitor">MONITOR</option>
</select>
</div>
<div class="form-row" id="node-input-timerow">
Expand All @@ -93,6 +104,14 @@
<option value="h">hour(s)</option>
</select>
</div>
<div class="form-row" id="node-input-deadband">
<label for="node-input-deadbandlabel"><i class="icon-time"></i> Deadband Type</label>
<input type="number" id="node-input-deadbandvalue" placeholder="number" style="max-width:120px">
<select id="node-input-deadbandtype" style="max-width:160px">
<option value="a">Absolute</option>
<option value="p">Percent</option>
</select>
</div>
<div class="form-row" id="node-input-servercertificate">
<label for="node-input-location"><i class="icon-time"></i> Certificate</label>
<select id="node-input-certificate" style="width:72%;">
Expand Down Expand Up @@ -121,7 +140,8 @@
<li>Subscribe</li>
<li>Unsubscribe</li>
<li>Event</li>
<li>Info</li>
<li>Info</li>
<li>Monitor</li>
</ul>
</p>
<p>Server certificate options are:</p>
Expand All @@ -142,4 +162,5 @@
Every inject subscribes a new monitored Item.</p>
<p>Interval is setting for Client <strong>requestedPublishingInterval</strong> (default: 100ms).</p>
<p>Input <strong>msg.payload</strong> can bring an samplingInterval for monitored Items/Events in subscription (default: 100ms).</p>
<p>Monitor will ask additional parameters like Absolute or Percent and deadband value</p>
</script>
164 changes: 163 additions & 1 deletion opcua/102-opcuaclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@ module.exports = function (RED) {
var AttributeIds = opcua.AttributeIds;
var read_service = require("node-opcua-service-read");
var TimestampsToReturn = read_service.TimestampsToReturn;
var subscription_service = require("node-opcua-service-subscription");

function OpcUaClientNode(n) {
RED.nodes.createNode(this, n);
this.name = n.name;
this.action = n.action;
this.time = n.time;
this.timeUnit = n.timeUnit;
this.deadbandtype = n.deadbandtype;
this.deadbandvalue = n.deadbandvalue;
this.certificate = n.certificate; // n == NONE, l == Local file, e == Endpoint, u == Upload
this.localfile = n.localfile; // Local file
// this.upload = n.upload; // Upload
Expand Down Expand Up @@ -420,6 +423,9 @@ module.exports = function (RED) {
case "subscribe":
subscribe_action_input(msg);
break;
case "monitor":
monitor_action_input(msg);
break;
case "unsubscribe":
unsubscribe_action_input(msg);
break;
Expand Down Expand Up @@ -545,7 +551,7 @@ module.exports = function (RED) {
node.client.keepSessionAlive = true;
var session = new ClientSession(node.client);
var proxyManager = new UAProxyManager(node.session);
console.log(nodeId.toString());
// console.log(nodeId.toString());
proxyManager.getObject(nodeId.toString(), function (err, data) {
if (!err) {
if (data.typeDefinition != "FolderType") {
Expand Down Expand Up @@ -652,6 +658,26 @@ module.exports = function (RED) {
}
}

function monitor_action_input(msg) {
verbose_log("monitoring");
if (!subscription) {
// first build and start subscription and subscribe on its started event by callback
var timeMilliseconds = opcuaBasics.calc_milliseconds_by_time_and_unit(node.time, node.timeUnit);
subscription = make_subscription(monitor_monitoredItem, msg, opcuaBasics.getSubscriptionParameters(timeMilliseconds));
} else {
// otherwise check if its terminated start to renew the subscription
if (subscription.subscriptionId != "terminated") {
set_node_status_to("active monitoring");
monitor_monitoredItem(subscription, msg);
} else {
subscription = null;
monitoredItems.clear();
set_node_status_to("terminated");
reset_opcua_client(connect_opcua_client);
}
}
}

function unsubscribe_action_input(msg) {
verbose_log("unsubscribing");
if (!subscription) {
Expand Down Expand Up @@ -784,6 +810,142 @@ module.exports = function (RED) {
return monitoredItem;
}

function monitor_monitoredItem(subscription, msg) {
verbose_log("Session subscriptionId: " + subscription.subscriptionId);
var nodeStr = msg.topic;
var dTypeIndex = nodeStr.indexOf(";datatype=");
if (dTypeIndex > 0) {
nodeStr = nodeStr.substring(0, dTypeIndex);
}
var monitoredItem = monitoredItems.get(msg.topic); // {"topicName": msg.topic});
if (!monitoredItem) {
var interval = convertAndCheckInterval(msg.payload);
verbose_log(msg.topic + " samplingInterval " + interval);
verbose_warn("Monitoring: " + msg.topic + ' by interval of ' + interval + " ms");
verbose_log("Deadband type (a==absolute, p==percent) " + node.deadbandtype + " deadband value " + node.deadbandvalue);
// Validate nodeId
try {
var nodeId = coerceNodeId(nodeStr);
if (nodeId && nodeId.isEmpty()) {
node_error(" Invalid empty node in getObject");
}
//makeNodeId(nodeStr); // above is enough
} catch (err) {
node_error(err);
return;
}
var deadbandtype = subscription_service.DeadbandType.Absolute;
// NOTE differs from standard subscription monitor
if (node.deadbandType == "a") {
deadbandType = subscription_service.DeadbandType.Absolute;
}
if (node.deadbandType == "a") {
deadbandType = subscription_service.DeadbandType.Percent;
}
var dataChangeFilter = new subscription_service.DataChangeFilter({
trigger: subscription_service.DataChangeTrigger.StatusValue,
deadbandType: deadbandtype,
deadbandValue: node.deadbandvalue
});
/*
var monitoredItemCreateRequest1 = new subscription_service.MonitoredItemCreateRequest({
itemToMonitor: {
nodeId: nodeStr,
attributeId: opcua.AttributeIds.Value
},
monitoringMode: subscription_service.MonitoringMode.Reporting,
requestedParameters: {
queueSize: 10,
samplingInterval: 100,
filter: new subscription_service.DataChangeFilter({
trigger: subscription_service.DataChangeTrigger.Status,
deadbandType: deadbandType,
deadbandValue: node.deadbandValue // from UI n.deadbandvalue
})
}
});
verbose_log("Monitoring parameters: " + monitoredItemCreateRequest1);
monitoredItem = subscription.createMonitoredItem(addressSpace, TimestampsToReturn.Both, monitoredItemCreateRequest1);
*/

monitoredItem = subscription.monitor({
nodeId: nodeStr,
attributeId: opcua.AttributeIds.Value
}, {
samplingInterval: interval,
queueSize: 10,
discardOldest: true,
filter: dataChangeFilter
},
TimestampsToReturn.Both, // Other valid values: Source | Server | Neither | Both
function (err) {
if (err) {
node_error("Check topic format for nodeId:" + msg.topic)
node_error('subscription.monitorItem:' + err);
// reset_opcua_client(connect_opcua_client); // not actually needed
} else {
monitoredItems.set({
"topicName": nodeStr,
mItem: monitoredItem
}); // Set use add
}
}
);

monitoredItem.on("initialized", function () {
verbose_log("initialized monitoredItem on " + nodeStr);
});

monitoredItem.on("changed", function (dataValue) {
set_node_status_to("active monitoring");
verbose_log(msg.topic + " value has changed to " + dataValue.value.value);
verbose_log(dataValue.toString());
if (dataValue.statusCode === opcua.StatusCodes.Good) {
verbose_log("\tStatus-Code:" + (dataValue.statusCode.toString(16)).green.bold);
} else {
verbose_log("\tStatus-Code:" + dataValue.statusCode.toString(16));
}

// Check if timestamps exists otherwise simulate them
if (dataValue.serverTimestamp != null) {
msg.serverTimestamp = dataValue.serverTimestamp;
msg.serverPicoseconds = dataValue.serverPicoseconds;
} else {
msg.serverTimestamp = new Date().getTime();;
msg.serverPicoseconds = 0;
}

if (dataValue.sourceTimestamp != null) {
msg.sourceTimestamp = dataValue.sourceTimestamp;
msg.sourcePicoseconds = dataValue.sourcePicoseconds;
} else {
msg.sourceTimestamp = new Date().getTime();;
msg.sourcePicoseconds = 0;
}

msg.payload = dataValue.value.value;
node.send(msg);
});

monitoredItem.on("keepalive", function () {
verbose_log("keepalive monitoredItem on " + nodeStr);
});

monitoredItem.on("terminated", function () {
verbose_log("terminated monitoredItem on " + nodeStr);
if (monitoredItems.get({
"topicName": nodeStr
})) {
monitoredItems.delete({
"topicName": nodeStr
});
}
});
}

return monitoredItem;
}

function unsubscribe_monitoredItem(subscription, msg) {
verbose_log("Session subscriptionId: " + subscription.subscriptionId);
var nodeStr = msg.topic;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "node-red-contrib-opcua",
"version": "0.2.41",
"version": "0.2.42",
"description": "A Node-RED node to communicate via OPC UA based on node-opcua library.",
"repository": {
"type": "git",
Expand Down

0 comments on commit 40f0c42

Please sign in to comment.