async function main() { const optionsInitial = { endpoint_must_exist: false, keepSessionAlive: true, connectionStrategy: { initialDelay: 2000, maxDelay: 10 * 1000, maxRetry: 10 } }; client = OPCUAClient.create(optionsInitial); client.on("backoff", (retry: number, delay: number) => { console.log(chalk.bgWhite.yellow("backoff attempt #"), retry, " retrying in ", delay / 1000.0, " seconds"); }); console.log(" connecting to ", chalk.cyan.bold(endpointUrl)); console.log(" strategy", client.connectionStrategy); await client.connect(endpointUrl); const endpoints = await client.getEndpoints(); if (argv.debug) { fs.writeFileSync("tmp/endpoints.log", JSON.stringify(endpoints, null, " ")); console.log(treeify.asTree(endpoints, true)); } const table = new Table(); let serverCertificate: Certificate; let i = 0; for (const endpoint of endpoints) { table.cell("endpoint", endpoint.endpointUrl + ""); table.cell("Application URI", endpoint.server.applicationUri); table.cell("Product URI", endpoint.server.productUri); table.cell("Application Name", endpoint.server.applicationName.text); table.cell("Security Mode", endpoint.securityMode.toString()); table.cell("securityPolicyUri", endpoint.securityPolicyUri); table.cell("Type", ApplicationType[endpoint.server.applicationType]); table.cell("certificate", "..." /*endpoint.serverCertificate*/); endpoint.server.discoveryUrls = endpoint.server.discoveryUrls || []; table.cell("discoveryUrls", endpoint.server.discoveryUrls.join(" - ")); serverCertificate = endpoint.serverCertificate; const certificate_filename = path.join(__dirname, "../certificates/PKI/server_certificate" + i + ".pem"); if (serverCertificate) { fs.writeFile(certificate_filename, toPem(serverCertificate, "CERTIFICATE"), () => {/**/ }); } table.newRow(); i++; } console.log(table.toString()); for (const endpoint of endpoints) { console.log("Identify Token for : Security Mode=", endpoint.securityMode.toString(), " Policy=", endpoint.securityPolicyUri); const table2 = new Table(); for (const token of endpoint.userIdentityTokens!) { table2.cell("policyId", token.policyId); table2.cell("tokenType", token.tokenType.toString()); table2.cell("issuedTokenType", token.issuedTokenType); table2.cell("issuerEndpointUrl", token.issuerEndpointUrl); table2.cell("securityPolicyUri", token.securityPolicyUri); table2.newRow(); } console.log(table2.toString()); } await client.disconnect(); // reconnect using the correct end point URL now console.log(chalk.cyan("Server Certificate :")); console.log(chalk.yellow(hexDump(serverCertificate!))); const options = { securityMode, securityPolicy, serverCertificate, defaultSecureTokenLifetime: 40000, endpoint_must_exist: false, connectionStrategy: { initialDelay: 2000, maxDelay: 10 * 1000, maxRetry: 10 } }; console.log("Options = ", options.securityMode.toString(), options.securityPolicy.toString()); client = OPCUAClient.create(options); console.log(" reconnecting to ", chalk.cyan.bold(endpointUrl)); await client.connect(endpointUrl); let userIdentity: any; // anonymous if (argv.userName && argv.password) { userIdentity = { password: argv.password, userName: argv.userName }; } the_session = await client.createSession(userIdentity); client.on("connection_reestablished", () => { console.log(chalk.bgWhite.red(" !!!!!!!!!!!!!!!!!!!!!!!! CONNECTION RE-ESTABLISHED !!!!!!!!!!!!!!!!!!!")); }); console.log(chalk.yellow(" session created")); console.log(" sessionId : ", the_session.sessionId.toString()); client.on("backoff", (retry: number, delay: number) => { console.log(chalk.bgWhite.yellow("backoff attempt #"), retry, " retrying in ", delay / 1000.0, " seconds"); }); client.on("start_reconnection", () => { console.log(chalk.bgWhite.red(" !!!!!!!!!!!!!!!!!!!!!!!! Starting Reconnection !!!!!!!!!!!!!!!!!!!")); }); // ----------------------------------------------------------------------------------------------------------- // NAMESPACE // display namespace array // ----------------------------------------------------------------------------------------------------------- const server_NamespaceArray_Id = makeNodeId(VariableIds.Server_NamespaceArray); // ns=0;i=2006 const dataValue = await the_session.readVariableValue(server_NamespaceArray_Id); console.log(" --- NAMESPACE ARRAY ---"); const namespaceArray = dataValue.value.value; for (const namespace of namespaceArray) { console.log(" Namespace ", namespace.index, " : ", namespace); } console.log(" -----------------------"); // ----------------------------------------------------------------------------------------------------------- // enumerate all EVENT TYPES // ----------------------------------------------------------------------------------------------------------- const result = getAllEventTypes(the_session); console.log(chalk.cyan("---------------------------------------------------- All Event Types ")); console.log(treeify.asTree(result, true)); console.log(" -----------------------"); // ----------------------------------------------------------------------------------------------------------- // Node Crawling // ----------------------------------------------------------------------------------------------------------- let t1: number; let t2: number; function print_stat() { t2 = Date.now(); const str = util.format("R= %d W= %d T=%d t= %d", client.bytesRead, client.bytesWritten, client.transactionsPerformed, (t2 - t1)); console.log(chalk.yellow.bold(str)); } if (doCrawling) { assert(_.isObject(the_session)); const crawler = new NodeCrawler(the_session); let t5 = Date.now(); client.on("send_request", () => { t1 = Date.now(); }); client.on("receive_response", print_stat); t5 = Date.now(); // xx crawler.on("browsed", function (element) { // xx console.log("->",(new Date()).getTime()-t,element.browseName.name,element.nodeId.toString()); // xx }); const nodeId = "ObjectsFolder"; console.log("now crawling object folder ...please wait..."); const obj = await crawler.read(nodeId); console.log(" Time = ", (new Date()).getTime() - t5); console.log(" read = ", crawler.readCounter); console.log(" browse = ", crawler.browseCounter); console.log(" browseNext = ", crawler.browseNextCounter); console.log(" transaction = ", crawler.transactionCounter); if (false) { // todo : treeify.asTree performance is *very* slow on large object, replace with better implementation // xx console.log(treeify.asTree(obj, true)); treeify.asLines(obj, true, true, (line: string) => { console.log(line); }); } } client.removeListener("receive_response", print_stat); // ----------------------------------------------------------------------------------------------------------------- // enumerate all Condition Types exposed by the server // ----------------------------------------------------------------------------------------------------------------- console.log("--------------------------------------------------------------- Enumerate all Condition Types exposed by the server"); const conditionTree = await enumerateAllConditionTypes(the_session); console.log(treeify.asTree(conditionTree)); console.log(" -----------------------------------------------------------------------------------------------------------------"); // ----------------------------------------------------------------------------------------------------------------- // enumerate all objects that have an Alarm & Condition instances // ----------------------------------------------------------------------------------------------------------------- const alarms = await enumerateAllAlarmAndConditionInstances(the_session); console.log(" -------------------------------------------------------------- Alarms & Conditions ------------------------"); for (const alarm of alarms) { console.log( "parent = ", chalk.cyan(w(alarm.parent.toString(), 30)), chalk.green.bold(w(alarm.typeDefinitionName, 30)), "alarmName = ", chalk.cyan(w(alarm.browseName.toString(), 30)), chalk.yellow(w(alarm.alarmNodeId.toString(), 40)) ); } console.log(" -----------------------------------------------------------------------------------------------------------------"); // ----------------------------------------------------------------------------------------------------------------- // Testing if server implements QueryFirst // ----------------------------------------------------------------------------------------------------------------- try { console.log(" ---------------------------------------------------------- Testing QueryFirst"); const queryFirstRequest: QueryFirstRequestOptions = { view: { viewId: NodeId.nullNodeId }, nodeTypes: [ { typeDefinitionNode: makeExpandedNodeId("i=58"), includeSubTypes: true, dataToReturn: [{ attributeId: AttributeIds.AccessLevel, relativePath: undefined }] } ]}; const queryFirstResult = await the_session.queryFirst(queryFirstRequest); console.log(" -----------------------------------------------------------------------------------------------------------------"); } catch (err) { console.log(" Server is not supporting queryFirst err=",err.message); } // create Read if (doHistory) { console.log(" ---------------------------------------------------------- History Read------------------------"); const now = Date.now(); const start = now - 1000; // read 1 seconds of history const historicalReadResult = await the_session.readHistoryValue({ nodeId: monitored_node }, new Date(start), new Date(now)); console.log(" -----------------------------------------------------------------------------------------------------------------"); } // ---------------------------------------------------------------------------------- // create subscription // ---------------------------------------------------------------------------------- console.log(" ---------------------------------------------------------- Create Subscription "); const parameters = { maxNotificationsPerPublish: 10, priority: 10, publishingEnabled: true, requestedLifetimeCount: 1000, requestedMaxKeepAliveCount: 12, requestedPublishingInterval: 2000 }; the_subscription = await the_session.createSubscription2(parameters); let t = getTick(); console.log("started subscription :", the_subscription!.subscriptionId); console.log(" revised parameters "); console.log(" revised maxKeepAliveCount ", the_subscription!.maxKeepAliveCount, " ( requested ", parameters.requestedMaxKeepAliveCount + ")"); console.log(" revised lifetimeCount ", the_subscription!.lifetimeCount, " ( requested ", parameters.requestedLifetimeCount + ")"); console.log(" revised publishingInterval ", the_subscription!.publishingInterval, " ( requested ", parameters.requestedPublishingInterval + ")"); the_subscription.on("internal_error", (err: Error) => { console.log(" received internal error", err.message); }).on("keepalive", () => { const t4 = getTick(); const span = t4 - t; t = t4; console.log("keepalive ", span / 1000, "sec", " pending request on server = ", (the_subscription as any).getPublishEngine().nbPendingPublishRequests); }).on("terminated", () => { /* */ }); try { const results1 = await the_session.getMonitoredItems(the_subscription.subscriptionId); console.log("MonitoredItems clientHandles", results1.clientHandles); console.log("MonitoredItems serverHandles", results1.serverHandles); } catch(err) { console.log("Server doesn't seems to implement getMonitoredItems method ", err.message); } // get_monitored_item // monitor_a_variable_node_value console.log("Monitoring monitor_a_variable_node_value"); // --------------------------------------------------------------- // monitor a variable node value // --------------------------------------------------------------- console.log(" Monitoring node ", monitored_node.toString()); const monitoredItem = ClientMonitoredItem.create( the_subscription, { attributeId: AttributeIds.Value, nodeId: monitored_node }, { discardOldest: true, queueSize: 10000, samplingInterval: 1000 // xx filter: { parameterTypeId: "ns=0;i=0", encodingMask: 0 }, } ); monitoredItem.on("initialized", () => { console.log("monitoredItem initialized"); }); monitoredItem.on("changed", (dataValue1: DataValue) => { console.log(monitoredItem.itemToMonitor.nodeId.toString(), " value has changed to " + dataValue1.value.toString()); }); monitoredItem.on("err", (err_message: string) => { console.log(monitoredItem.itemToMonitor.nodeId.toString(), chalk.red(" ERROR"), err_message); }); const results = await the_session.getMonitoredItems(the_subscription.subscriptionId); console.log("MonitoredItems clientHandles", results.clientHandles); console.log("MonitoredItems serverHandles", results.serverHandles); console.log("Monitoring monitor_the_object_events"); // --------------------------------------------------------------- // monitor the object events // --------------------------------------------------------------- const baseEventTypeId = "i=2041"; // BaseEventType; const serverObjectId = "i=2253"; const fields = [ "EventId", "EventType", "SourceNode", "SourceName", "Time", "ReceiveTime", "Message", "Severity", // ConditionType "ConditionClassId", "ConditionClassName", "ConditionName", "BranchId", "Retain", "EnabledState", "Quality", "LastSeverity", "Comment", "ClientUserId", // AcknowledgeConditionType "AckedState", "ConfirmedState", // AlarmConditionType "ActiveState", "InputNode", "SuppressedState", "HighLimit", "LowLimit", "HighHighLimit", "LowLowLimit", "Value" ]; const eventFilter = constructEventFilter(fields, [ resolveNodeId("ConditionType") ]); const event_monitoringItem = ClientMonitoredItem.create( the_subscription, { attributeId: AttributeIds.EventNotifier, nodeId: serverObjectId }, { discardOldest: true, filter: eventFilter, queueSize: 100000 } ); event_monitoringItem.on("initialized", () => { console.log("event_monitoringItem initialized"); }); event_monitoringItem.on("changed", (eventFields: Variant[]) => { dumpEvent(the_session, fields, eventFields); }); event_monitoringItem.on("err", (err_message: string) => { console.log(chalk.red("event_monitoringItem ", baseEventTypeId, " ERROR"), err_message); }); console.log("--------------------------------------------- Monitoring alarms"); const alarmNodeId = coerceNodeId("ns=2;s=1:Colours/EastTank?Green"); await monitorAlarm(the_subscription, alarmNodeId); console.log("Starting timer ", timeout); if (timeout > 0) { // simulate a connection break at t =timeout/2 // new Promise((resolve) => { setTimeout(() => { console.log(chalk.red(" -------------------------------------------------------------------- ")); console.log(chalk.red(" -- SIMULATE CONNECTION BREAK -- ")); console.log(chalk.red(" -------------------------------------------------------------------- ")); const socket = (client as any)._secureChannel._transport._socket; socket.end(); socket.emit("error", new Error("ECONNRESET")); }, timeout / 2.0); // }); await new Promise((resolve) => { setTimeout(async () => { console.log("time out => shutting down "); if (!the_subscription) { return resolve(); } if (the_subscription) { const s = the_subscription; the_subscription = null; await s.terminate(); await the_session.close(); await client.disconnect(); console.log(" Done "); process.exit(0); } }, timeout); }); } console.log(" closing session"); await the_session.close(); console.log(" session closed"); console.log(" Calling disconnect"); await client.disconnect(); console.log(chalk.cyan(" disconnected")); console.log("success !! "); }
treeify.asTree( { apples: 'gala', // ├─ apples: gala oranges: 'mandarin' // └─ oranges: mandarin }, true, true ); treeify.asLines( { apples: 'gala', // ├─ apples: gala oranges: 'mandarin', // ├─ oranges: mandarin grapes: { // └─ grapes seedless: 'Thompson, Selma Pete', // ├─ seedless: Thompson, Selma Pete seeded: 'concord' // └─ seeded: Concord } }, true, log ); treeify.asLines( { apples: 'gala', // ├─ apples: gala oranges: 'mandarin' // └─ oranges: mandarin }, true, false, log );