Last active
December 16, 2020 20:32
-
-
Save bitflower/77529de473246498d3a5c4fab5f31f34 to your computer and use it in GitHub Desktop.
node-opcua server for API binding with configuration via JSON file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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" | |
} | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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