Skip to content

Instantly share code, notes, and snippets.

@bitflower
Last active December 16, 2020 20:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bitflower/77529de473246498d3a5c4fab5f31f34 to your computer and use it in GitHub Desktop.
Save bitflower/77529de473246498d3a5c4fab5f31f34 to your computer and use it in GitHub Desktop.
node-opcua server for API binding with configuration via JSON file
import { OPCUAClient, OPCUAClientOptions } from 'node-opcua';
import { CO_OPCUA_CLIENT_DEFAULTS } from './client-defaults';
import { CoOPCUAClientConfig } from '../config';
let client: OPCUAClient;
export const createClient = (options: CoOPCUAClientConfig) => {
const optionsCombined: OPCUAClientOptions = {
...CO_OPCUA_CLIENT_DEFAULTS,
applicationName: options.name,
...(options.requestedSessionTimeout && {
requestedSessionTimeout: options.requestedSessionTimeout
})
};
client = OPCUAClient.create(optionsCombined);
return client;
};
export const connectClient = async (endpointUrl: string) => {
await client.connect(endpointUrl);
};
{
"server": {
},
"clients": [
{
"url": "opc.tcp://10.144.64.160:4840",
"requestedSessionTimeout": 5000
}
],
"subscriptions": [
{
"nodeId": "ns=3;s=\"dbDataSend\".\"strBaleCnt\"[0].iQuantity",
"attributeId": "Value",
"timeout": 0,
"mapping": {
"value": "value.value",
"timestamp": "serverTimestamp"
}
}
]
}
// REFERNCE: https://github.com/node-opcua/node-opcua/blob/v2.1.3/documentation/creating_a_client_typescript.md
import { ClientMonitoredItem } from 'node-opcua';
import { connectClient, createClient } from '../client';
import {
createHandleMonitoredItemChanged,
makeSubscription
} from '../subscription';
import { getConfig } from '../utils';
import { createSession } from './session';
export const startServer = async () => {
const config = await getConfig();
const { clients, server, subscriptions } = config;
if (!server) {
console.log(`No "server" configuration found in config file.`, { config });
return;
}
connectServer({
url: server.url
});
try {
// Setup API server ...
} catch (error) {
console.log('API Error', error);
}
if (!clients) {
console.log(`No "clients" found in config file.`, { config });
return;
}
// Pick client 1 for now
const clientConfig = clients[0];
const client = createClient(clientConfig);
try {
await connectClient(clientConfig.url);
console.log(`Connected to OPC UA server "${clientConfig.url}".`);
} catch (e) {
console.log(`Error connecting to OPC UA server "${clientConfig.url}".`, e);
process.exit(0);
}
const session = await createSession(client);
const monitoredItems: ClientMonitoredItem[] = [];
for (const sub of subscriptions) {
const monitoredItem = await makeSubscription(session, sub);
monitoredItem.on('changed', createHandleMonitoredItemChanged(sub, session));
monitoredItems.push(monitoredItem);
}
process.on('SIGTERM', async () => {
console.log(
'Closing subscriptions, session, client and CaseOS connection.'
);
for (const sub of monitoredItems) {
await sub.terminate();
}
await session.close();
console.log('Session closed');
await client.disconnect();
console.log('Client disconnected !');
process.exit(0);
});
};
import {
AttributeIds,
makeBrowsePath,
ClientSubscription,
TimestampsToReturn,
MonitoringParametersOptions,
ReadValueIdLike,
ClientMonitoredItem,
ClientSession
} from 'node-opcua';
import { CoOPCUASubscriptionConfig } from '../config';
export const makeSubscription = async (
session: ClientSession,
subscriptionConfig: CoOPCUASubscriptionConfig
): Promise<ClientMonitoredItem> => {
const { browsePath, nodeId } = subscriptionConfig;
let foundNodeId: string = '';
// OPTION A: Get nodeId from a browse result
if (browsePath) {
const madeBrowsePath = makeBrowsePath('RootFolder', browsePath);
const result = await session.translateBrowsePath(madeBrowsePath);
console.log('translateBrowsePath => results', result);
foundNodeId = result.targets[0].targetId.toString();
console.log(' expandedNodeId nodeId = ', foundNodeId);
}
// OPTION B: nodeId was provided
if (nodeId) {
foundNodeId = nodeId;
}
const subscription = await session.createSubscription2({
requestedPublishingInterval: 1000, // TODO: add to config
requestedLifetimeCount: 60,
requestedMaxKeepAliveCount: 20,
maxNotificationsPerPublish: 100,
publishingEnabled: false,
priority: 0
});
// const subscription = ClientSubscription.create(session, {
// requestedPublishingInterval: 1000, // TODO: add to config
// requestedLifetimeCount: 60,
// requestedMaxKeepAliveCount: 20,
// maxNotificationsPerPublish: 100,
// publishingEnabled: true,
// priority: 0
// });
subscription
.on('subscription started', function () {
console.log(
`subscription started, subscriptionId=`,
subscription.subscriptionId
);
})
.on('subscription keepalive', function () {
console.log('keepalive');
})
.on('subscription error', function (e) {
console.log('error', e);
})
.on('subscription terminated', function () {
console.log('terminated');
})
.on('subscription raw_notification', (n) => {
console.log(n.toString());
});
// install monitored item
const itemToMonitor: ReadValueIdLike = {
nodeId,
attributeId: (<any>AttributeIds)[subscriptionConfig.attributeId]
};
const parameters: MonitoringParametersOptions = {
samplingInterval: 1000,
discardOldest: false,
queueSize: 1
};
const monitoredItem = await subscription.monitor(
itemToMonitor,
parameters,
TimestampsToReturn.Both
);
// const monitoredItem = ClientMonitoredItem.create(
// subscription,
// itemToMonitor,
// parameters,
// TimestampsToReturn.Both
// );
monitoredItem.on('initialized', function () {
console.log('monitoredItem initialized');
});
monitoredItem.on('err', function (err_message) {
console.log(monitoredItem.itemToMonitor.nodeId.toString(), err_message);
});
return monitoredItem;
//TODO: .terminate() on subscription ?
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment