Multiple variables in one subscription - node-opcua

I've read that it is better practice to put multiple variables into one subscription compared to creating more subscriptions.
I'm using this example (https://github.com/node-opcua/node-opcua-htmlpanel) and want to read more variables in subscription.
My code responsible for reading one variable - one node.
const nodeIdToMonitor = 'ns=3;s="OP_UA_FB_DB"."OPC_Data"."Data_integer"';
const itemToMonitor = {
nodeId: nodeIdToMonitor,
attributeId: AttributeIds.Value,
};
const parameters = {
samplingInterval: 100,
discardOldest: true,
queueSize: 100,
};
const monitoredItem = await subscription.monitor(
itemToMonitor,
parameters,
TimestampsToReturn.Both
);
monitoredItem.on("changed", (dataValue) => {
io.sockets.emit("message", {
value: dataValue.value.value,
timestamp: dataValue.serverTimestamp,
nodeId: nodeIdToMonitor,
browseName: "ISP",
});
});

To monitor multiple variables in one subscription you can make use of the monitorItems method call. This method call takes an array of nodes to be monitored as input and create monitored items for all the nodes present in the array.
Here is a sample code snippet that you can make use of to monitor multiple nodes,
const parameters1: MonitoringParametersOptions = {
discardOldest: true,
queueSize: 100,
samplingInterval: 100,
filter: new DataChangeFilter({
deadbandType: DeadbandType.Absolute,
deadbandValue: 0.1,
trigger: DataChangeTrigger.StatusValueTimestamp,
}),
};
const itemsToMonitor2: ReadValueIdOptions[] = [
{
attributeId: AttributeIds.Value,
nodeId: nodeIdToMonitor
},
{
attributeId: AttributeIds.Value,
nodeId: nodeIdToMonitor1
}];
const item2 = (await subscription.monitorItems(
itemsToMonitor2,
parameters1,
TimestampsToReturn.Both))
// If a data change is detected print the value
item2.on("changed", (monitoredItem: ClientMonitoredItemBase, dataValue: DataValue, index: number) => {
console.log("Index", index);
console.log(" The value has changed : ", dataValue.toString());
});
I see that you are using open source OPC UA implementations. Here are a list open source implementations that you might be interested in trying out:
open62541 – https://open62541.org/certified-sdk.html – C stack | Mozilla License | Embedded ready | TSN ready
NodeOPCUA – https://node-opcua.github.io/ – NodeJS | MIT License | Cloud ready
FreeOpcUa – https://github.com/FreeOpcUa/freeopcua – C++ | LGPL-3.0 License | Python bindings
UA .NET stack – https://github.com/OPCFoundation/UA-.NETStandard – RCL License for corporate members of OPC Foundation & GPL 2.0 for others | Standard profile | Web oriented implementation
If you are looking for more hands-on information, you can also check out these resources:
Free documentation: You can look at the open source documentation page: https://open62541.org/doc/current/
Paid online course: Practical introduction to OPC UA – code walk-through and examples in this course use the open62541 stack: https://opcfoundation.org/products/view/practical-introduction-to-opc-ua-part-i

Related

Unable to deploy smart contract to testnet (Ropsten) using Truffle Infura

I'm trying to deploy a simple smart contract to Testnet Ropsten but always fails with the following error:
Error: *** Deployment Failed ***
"Migrations" -- Transaction was not mined within 750 seconds, please
make sure your transaction was properly sent. Be aware that it might
still be mined!.
I've searched in many places, and it says to change the gas value, I tried almost every value but still got the same error. Sometimes it says that the gas value is not enough and sometimes it's too much.
This is my code:
require("dotenv").config();
const HDWalletProvider = require("truffle-hdwallet-provider");
const MNEMONIC = process.env.MNEMONIC;
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
ropsten: {
provider: function () {
return new HDWalletProvider(
MNEMONIC,
`https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}`
);
},
network_id: 3,
gas: 5500000, // Here I tried from 1000000 to 5500000
},
},
compilers: {
solc: {
version: "0.8.10",
},
},
};
Contract:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.3;
contract HelloWorld {
function sayHello() public pure returns(string memory){
return("hello world");
}
}
Migrations:
const Migrations = artifacts.require("Migrations");
module.exports = function (deployer) {
deployer.deploy(Migrations);
};
const HelloWorld = artifacts.require("HelloWorld");
module.exports = function (deployer) {
deployer.deploy(HelloWorld, "hello");
};
I have my metamask wallet with 1 ether in red ropstem
Any idea on how to deploy a smart contract in a testnet?
It might be because, you are not speciying address and gasPrice. gasPrice varies depending on network activities. You can get the current gasPrice here : https://ethgas.watch/
ropsten: {
provider: () =>
new HDWalletProvider({
mnemonic: {
phrase: MENMONICS,
},
providerOrUrl: `https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}`,
// first address
addressIndex: 0,
}),
network_id: 3,
gas: 5500000,
// this might varies depending on the network activity
gasPrice: 20000000000,
confirmations: 2, // number of blocks to wait between deployment
timeoutBlocks: 200, // number of blocks before deployment times out
},

Android Enterprises Device Enrollment Stuck with NodeJs Generated QR Code with Service Account Authentication

As mentioned in the google documents i have tested the following process
URL to quick start: https://colab.research.google.com/github/google/android-management-api-samples/blob/master/notebooks/quickstart.ipynb#scrollTo=pjHfDSb8BoBP
Create Enterprise
Create Policy
Enroll the device
Then I have used the NODEJS API of Android Enterprises to develop the server based solution, which is working fine as per the documentation for all the functions such as get, create, delete the policy, devices, enterprises.
The issue i am facing is with the QR code generated from NODE application, when i scan the QR code generated from NODEJS application, the device got stuck at system update.
Following is my Policy update function
router.post('/update/:id', async function(req, res) {
const {title,policy_body,update_mask,enroll_url} = req.body;
// here we are callng the android managment API to and then the response we will update to database
const amApiBody = {
name: policy_body.name,
updateMask:update_mask,
requestBody:policy_body
}
const policy_update_response = await amApi.updatePolicy(amApiBody);
const p = await policyModel.update(req.params.id,title,policy_update_response,enroll_url);
res.json(p)
});
AmAPI file
this.updatePolicy = async function (body)
{
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/androidmanagement'],
});
const authClient = await auth.getClient();
google.options({auth: authClient});
// Get the list of available policies
const res = await androidmanagement.enterprises.policies.patch(body);
console.log('requestFinalBody=',body);
return res.data;
}
Following is my policy data obtained by running above function
policy_create_response= {
name: 'enterprises/LC019rjnor/policies/policy1',
version: '14',
applications: [
{
packageName: 'com.google.samples.apps.iosched',
installType: 'FORCE_INSTALLED',
autoUpdateMode: 'AUTO_UPDATE_HIGH_PRIORITY'
},
{
packageName: 'com.dekaisheng.courier',
installType: 'FORCE_INSTALLED',
autoUpdateMode: 'AUTO_UPDATE_HIGH_PRIORITY'
}
],
keyguardDisabledFeatures: [ 'KEYGUARD_DISABLED_FEATURE_UNSPECIFIED' ],
defaultPermissionPolicy: 'GRANT',
uninstallAppsDisabled: true,
keyguardDisabled: true,
tetheringConfigDisabled: true,
dataRoamingDisabled: true,
networkEscapeHatchEnabled: true,
bluetoothDisabled: true,
debuggingFeaturesAllowed: true,
funDisabled: true,
kioskCustomLauncherEnabled: true
}
Note i have exported the variable to the terminal as follows before running the app, the auth.json is the service account credential file.
export GOOGLE_APPLICATION_CREDENTIALS="/Users/Mac/Projects/wajid/mdm/server/env/auth.json"
Thanks for the help in advance
I figured out that in nodeJS API I was passing wrong property name of Policy value in the request body.
Code before fix
parent: this.getParent(policyName),
requestBody:{
“name”: “my_policy"
}
Code after fix
parent: this.getParent(policyName),
requestBody:{
"policyName”: “my_policy"
}

Why is my Workbox GenerateSW showing my offline page while connected?

I'm trying to setup my offline page using Workbox GenerateSW() and running into an issue where on the first load after I clear site data and hard refresh displays my homepage, but on subsequent loads I am getting the offline page I set up even though I'm online. I have a multi page PHP app that has the assets served up by a CDN. I run the GenerateSW() task in a JS file called by an npm node script.
Here is my GenerateSW() code...
// Pull in .env file values...
const dotEnv = require('dotenv').config({ path: '/var/www/my-project/sites/www/.env' });
if (dotEnv.error) {
throw dotEnv.error
}
const {generateSW} = require('workbox-build');
// Used to break cache on generate of new SW where file is composed of multiple pieces that can't be watched.
const genRanHex = (size = 24) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
const mode = 'development';
generateSW({
swDest: './sites/www/public/service-worker.js',
skipWaiting: true,
clientsClaim: true,
cleanupOutdatedCaches: true,
cacheId: genRanHex(),
mode: mode,
navigateFallback: '/offline',
offlineGoogleAnalytics: mode === 'production',
globDirectory: './sites/assets/public',
globPatterns: [
'img/shell/**/*.{svg,png}',
'dist/**/*.{js,css}',
'manifest.json'
],
modifyURLPrefix: {
'dist/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/dist/`,
'img/shell/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/img/shell/`,
},
ignoreURLParametersMatching: [/v/],
additionalManifestEntries: [
{
"url": "/offline",
"revision": genRanHex()
}
],
runtimeCaching: []
}).then(({count, size}) => {
console.log(`Generated service worker, which will precache ${count} files, totaling ${size} bytes.`);
}).catch(console.error);
navigateFallback is not actually offline page. From workbox docs:
If specified, all navigation requests for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common App Shell HTML.
For offline page, this question might help.
So the accepted answer was correct in my misuse of navigateFallback which I was trying to use as an offline fallback for non cached routes. After some digging and tinkering, I found the correct way to go about it. The important part that I missed or was not documented well enough on Workbox is that the offline fallback happens at the runtimeCache level...
// Pull in .env file values...
const dotEnv = require('dotenv').config({ path: '/var/www/my-project/sites/www/.env' });
if (dotEnv.error) {
throw dotEnv.error
}
const {generateSW} = require('workbox-build');
// Used to break cache on generate of new SW where file is composed of multiple pieces that can't be watched.
const genRanHex = (size = 24) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
const mode = 'development';
generateSW({
swDest: './sites/www/public/service-worker.js',
skipWaiting: true,
clientsClaim: true,
cleanupOutdatedCaches: true,
cacheId: genRanHex(),
mode: mode,
offlineGoogleAnalytics: mode === 'production',
globDirectory: './sites/assets/public',
globPatterns: [
'img/shell/**/*.{svg,png}',
'dist/**/*.{js,css}',
'manifest.json'
],
modifyURLPrefix: {
'dist/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/dist/`,
'img/shell/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/img/shell/`,
},
ignoreURLParametersMatching: [/v/],
additionalManifestEntries: [
{
"url": "/offline",
"revision": genRanHex()
}
],
runtimeCaching: [
{
urlPattern: /^https:\/\/([\w+\.\-]+www\.mysite\.tv)(|\/.*)$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'core',
precacheFallback: {
fallbackURL: '/offline' // THIS IS THE KEY
}
}
}
]
}).then(({count, size}) => {
console.log(`Generated service worker, which will precache ${count} files, totaling ${size} bytes.`);
}).catch(console.error);

React Testing Library for Actions in redux

I am new to React Testing Library and have issue with actions.
Can anyone please guide me
I have tried below code and its giving error Received: [Function anonymous]
export const openText = () => (dispatch: Dispatch) => {
dispatch({
type: actionTypes.Text,
payload: true
});
};
Test Case
it('open text() => {
const expectedAction = {
type: actionTypes.OPEN_Text,
payload:'value
};
const action = actions.openText('value);
expect(action).toEqual(expectedAction);
});
Error: It says Received: [Function anonymous]
React Testing Library, as the name implies, is used for testing React components. You do not need it in this case.
You seem to be trying to test a Redux action creator. I suggest you follow the async action creators section from the Redux docs.
Your test currently fails because you are expecting to receive an object but your action creator returns a function.

Node Opcua / QtOpcUa - Method Calls

I have a Node OPC Server which I connect to with a Qt application using the QtOpcUa client library.
On my server I define a method that's basically a crude historic access request as HDA support is not yet available, it takes in a start_date and end_date then queries a database for the relevant values which it returns in an array.
It looks a bit like this:
const deviceTrends = namespace.addObject({
organizedBy: deviceObject,
browseName: strings.TREND_NODE
})
const method = namespace.addMethod(deviceTrends,{
nodeId: strings.NSI + part.name + "-Trend",
browseName: part.name + "-Trend",
inputArguments: [
{
name:"start_date",
description: { text: "Trend Start Date" },
dataType: opcua.DataType.DateTime
},{
name:"end_date",
description: { text: "Trend End Date" },
dataType: opcua.DataType.DateTime
}
],
outputArguments: [{
name:"Trend",
description:{ text: "Trend Data from start_date to end_date" },
dataType: opcua.DataType.String ,
valueRank: 1
}]});
method.bindMethod(function(inputArguments,context,callback) {
console.log("called")
const start = inputArguments[0].value;
const end = inputArguments[1].value;
console.log("Start: ", start);
console.log("End: ", end);
let sql = `SELECT Date date,
Name name,
Value value
FROM Trends
WHERE DateTime >= ? AND DateTime <= ?`;
var result = []
db.each(sql, [start, end], (err, row) =>
{
result.push(`${row.date}: ${row.name} - ${row.value}`)
})
console.log(result)
const callMethodResult = {
statusCode: opcua.StatusCodes.Good,
outputArguments: [{
dataType: opcua.DataType.String,
arrayType: opcua.VariantArrayType.Array,
value :result
}]
};
callback(null,callMethodResult);});}
I can see this in a client such as Prosys and call the method which works okay:
However I can't seem to call this method from Qt, I've cut out the packaging of arguments and the result handler (it just lists out the received params):
QOpcUaNode* n = devices[deviceName].client->node("ns=1;s=Speed-Trend");
connect(n, &QOpcUaNode::methodCallFinished, [this, deviceName](QString methodNodeId, QVariant result, QOpcUa::UaStatusCode status)
{
qDebug() << " Response received ";
this->handleNodeTrendResponse(deviceName, methodNodeId, result, status);
});
n->callMethod(n->nodeId(), args);
Trace:
Requesting Trend: From QDateTime(2018-10-07 13:13:56.766 BST Qt::TimeSpec(LocalTime)) TO QDateTime(2018-10-07 13:14:05.390 BST Qt::TimeSpec(LocalTime))
qt.opcua.plugins.open62541: Could not call method: BadNodeIdInvalid
Response received [Output from method result handler]
Device Name: "speed-device"
Method Node Id: "ns=1;s=Speed-Trend"
Result: QVariant(Invalid)
Result to List: << ()
Status: QOpcUa::UaStatusCode(BadNodeIdInvalid)
I also can't seem to find the method on other clients too, this is from an OPC UA Client application on my phone which shows nothing under the Trends object:
Everything else seems accessible, I can request variables, setup monitoring all fine.
Is there something I'm just missing here or is it an issue with QtOpcUa and other clients?
I can work around this by creating variables instead to capture input and output arguments and a boolean to represent a method call but it's a lot neater to tie everything up in a single method.
Thanks