Node-OPCUA Node-OPCUA, the OPCUA SDK for node.js. https://node-opcua.github.io/ Wed, 16 Feb 2022 21:30:05 +0000 Wed, 16 Feb 2022 21:30:05 +0000 Jekyll v3.9.0 Node-OPCUA goes PubSub - Episode 1 <p><strong>Node-OPCUA goes PubSub ! Episode 1</strong></p> <p><img src="/images/2022-02-16/usecase1_diagram3.png" alt="use case" width="100%" /></p> <p>That’s it! NodeOPCUA now supports PubSub as per Part 14 of the official OPCUA specifications.</p> <p>In this article, we will extend an existing OPC-UA Server. Then we will turn it into a PubSub publisher that streams its sensor values to an external MQTT broker. A scenario where IoT edge devices have to publish data to the cloud for data analysis.</p> <p>If you are already familiar with <a href="https://node-opcua.github.io">Node-OPCUA</a> programming, you already know how to create a simple OPCUA Server. You can do this in 3 easy steps, assuming that you have already installed NodeJS on your computer.</p> <p>Let’s create a NodeJs project:</p> <pre><code class="language-{.bash}">mkdir my-publishing-opcua-server cd my-publishing-opcua-server npm init -y npm install node-opcua </code></pre> <p>Now let’s edit our <code class="language-plaintext highlighter-rouge">server.js</code> javascript file:</p> <h2 id="the-server">the server</h2> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">{</span> <span class="nx">OPCUAServer</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">);</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">some additionnal imports</span><span class="dl">"</span> <span class="p">(</span><span class="k">async</span><span class="p">()</span><span class="o">=&gt;</span><span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">server</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OPCUAServer</span><span class="p">({</span> <span class="na">port</span><span class="p">:</span> <span class="mi">26543</span> <span class="p">});</span> <span class="k">await</span> <span class="nx">server</span><span class="p">.</span><span class="nx">initialize</span><span class="p">();</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">add a temperature sensor</span><span class="dl">"</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">enable pub-sub service</span><span class="dl">"</span> <span class="k">await</span> <span class="nx">server</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">server started at </span><span class="dl">"</span><span class="p">,</span> <span class="nx">server</span><span class="p">.</span><span class="nx">getEndpointUrl</span><span class="p">());</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="nx">process</span><span class="p">.</span><span class="nx">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span> <span class="p">})();</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">constructing the configuration parameters</span><span class="dl">"</span> </code></pre></div></div> <p><a href="#" title="save:">server.js</a></p> <h3 id="add-a-temperature-sensor">add a temperature sensor</h3> <p>Let’s create a sensor with its temperature variables.</p> <p>Here is the OPCUA model diagram that represents the sensor option.</p> <p><img src="/images/2022-02-16/ea94ba4b1156f12d17d85fc4daad377c6ec15567.png" alt="" width="33%" /></p> <p>The javascript code to create the sensor instance is straightforward.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">namespace</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addressSpace</span><span class="p">.</span><span class="nx">getOwnNamespace</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">sensor</span> <span class="o">=</span> <span class="nx">namespace</span><span class="p">.</span><span class="nx">addObject</span><span class="p">({</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MySensor</span><span class="dl">"</span><span class="p">,</span> <span class="na">organizedBy</span><span class="p">:</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addressSpace</span><span class="p">.</span><span class="nx">rootFolder</span><span class="p">.</span><span class="nx">objects</span> <span class="p">});</span> <span class="kd">const</span> <span class="nx">temperature</span> <span class="o">=</span> <span class="nx">namespace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">nodeId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">s=Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">sensor</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">dataType</span><span class="p">:</span> <span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="mi">0</span> <span class="p">}</span> <span class="p">});</span> <span class="nl">Note</span><span class="p">:</span> <span class="nx">The</span> <span class="nx">temperature</span> <span class="nx">variable</span> <span class="nx">nodeId</span> <span class="nx">has</span> <span class="nx">been</span> <span class="kd">set</span> <span class="nx">to</span> <span class="s2">`"ns=1;s=Temperature"`</span><span class="p">.</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">simulate the temperature variation.</span><span class="dl">"</span> </code></pre></div></div> <h3 id="simulate-the-temperature-variation">simulate the temperature variation.</h3> <p>We do not have a physical sensor connected here. Instead, we can simulate a temperature variation by continuously changing the temperature with a sine wave.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">setInterval</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="mi">19</span> <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sin</span><span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">/</span> <span class="mi">10000</span><span class="p">)</span> <span class="o">+</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span><span class="o">*</span><span class="mf">0.2</span><span class="p">;</span> <span class="nx">temperature</span><span class="p">.</span><span class="nx">setValueFromSource</span><span class="p">({</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="nx">value</span> <span class="p">});</span> <span class="p">},</span> <span class="mi">100</span><span class="p">);</span> </code></pre></div></div> <h3 id="run-the-server">run the server</h3> <p>We can now start the server and check the temperature value using an OPCUA client.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ node server.js </code></pre></div></div> <p>We can monitor the value by using <a href="https://github.com/node-opcua/opcua-commander"><code class="language-plaintext highlighter-rouge">opcua-commander</code></a>. This lightweight OPC-UA client works in a console and does not require any GUI framework.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npx -y opcua-commander -e opc.tcp://localhost:26543 </code></pre></div></div> <p>We can now navigate to the variable <code class="language-plaintext highlighter-rouge">/RootFolder/Objects/MySensor/Temperature</code> to watch it change.</p> <p><img src="/images/2022-02-16/opcua-commander.png" alt="image" width="100%" /></p> <h3 id="adding-pub-sub-support">Adding PUB SUB support</h3> <p>We now want to publish the temperature to an MQTT Brocker using the OPCUA PubSub specification. MQTT is a generic Publish-Subscribe protocol that allows data publishers to send almost any data type to any related subscriber. It is very flexible and convenient but doesn’t rely upon enforcing the data payload format that is conveyed to the subscriber. When using OPCUA over MQTT PUBSUB, the payload can be a JSON object or a binary blob. It is also structured in a standard way, with the header, metadata, rich content possibility message signature, and encryption. We will focus here on the OPCUA JSON payload. Two types of OPCUA JSON encoding are possible; The first can be used to be consumed by a generic (non-OPCUA) application. The second format contains more meta-information about the data type transmitted, such as the variant type. This format can use it to synchronize two OPCUA applications (client or server) over MQTT. (we will come back to this point in a future article.)</p> <p>For the time being, let’s focus on adding the pubsub extension to our server, creating an MQTT JSON publisher, and creating a dataset writer that publishes our temperature value.</p> <p>The initial step is required to add the node-opcua pubsub server extension.</p> <p>We start by importing the <code class="language-plaintext highlighter-rouge">node-opcua-pubsub-server</code> and <code class="language-plaintext highlighter-rouge">node-opcua-pubsub-expander</code> modules to our project.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm install node-opcua-pubsub-server node-opcua-pubsub-expander </code></pre></div></div> <h3 id="enable-pub-sub-service">enable pub-sub service</h3> <p>We can now turn our OPCUA Server into a PubSub ready publisher by calling the <code class="language-plaintext highlighter-rouge">installPubSub</code> utility. This function will do the heavy-lifting of creating the PublishSubscribe object under the standard Server object in the address space and bind all the necessary methods and services.</p> <p>Let’s add this after the <code class="language-plaintext highlighter-rouge">await server.initialize();</code> of our server code.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">configuration</span> <span class="o">=</span> <span class="nx">getPubSubConfiguration</span><span class="p">();</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">configuration</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span> <span class="c1">//</span> <span class="k">await</span> <span class="nx">installPubSub</span><span class="p">(</span><span class="nx">server</span><span class="p">,</span> <span class="p">{</span> <span class="nx">configuration</span><span class="p">,</span> <span class="p">});</span> </code></pre></div></div> <h3 id="construct-the-configuration-parameters">construct the configuration parameters</h3> <p>We reed to provide a set of parameters to fully configure the publisher and the associated dataset. A dataset describes the variables that will be published in the payload.</p> <p>Let’s analyze the <code class="language-plaintext highlighter-rouge">getPubSubConfiguration</code> function that produces the configuration object.</p> <p>It returns a <code class="language-plaintext highlighter-rouge">PubSubConfigurationDataType</code> object containing the PubSub configuration. The configufation describes the connection and the published data set.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getPubSubConfiguration</span><span class="p">()</span> <span class="p">{</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">create the connection</span><span class="dl">"</span> <span class="kd">const</span> <span class="nx">connection</span> <span class="o">=</span> <span class="nx">createConnection</span><span class="p">();</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">create the published dataset</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">publishedDataSet</span> <span class="o">=</span> <span class="nx">createPublishedDataSet</span><span class="p">();</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">PubSubConfigurationDataType</span><span class="p">({</span> <span class="na">connections</span><span class="p">:</span> <span class="p">[</span><span class="nx">connection</span><span class="p">],</span> <span class="na">publishedDataSets</span><span class="p">:</span> <span class="p">[</span><span class="nx">publishedDataSet</span><span class="p">]</span> <span class="p">});</span> <span class="p">}</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">PubSubConfiguration</code> describes the connections and the published data sets.</p> <p>A connection contains specific parameters regarding the PubSub transport protocol used and the various required parameters for the broker or the broadcasting system.</p> <p>A published data set describes the payload content and provides the mapping information to link the published variables with the corresponding OPCUA variables in the server address space.</p> <p>In our case, we need to define an OPCUA JSON MQTT connection and a single dataset.</p> <h3 id="create-the-connection">create the connection</h3> <p>For a OPCUA MQTT JSON transport, we need to set the <code class="language-plaintext highlighter-rouge">transportProfileUri</code> to Transport.MQTT_JSON.</p> <p>The <code class="language-plaintext highlighter-rouge">PubSubConnection</code> contains the address of the MQTT broker we want to publish to. For our demo, we will use the public demo broker offered by <a href="https://www.hivemq.com/">Hivemq</a> at <code class="language-plaintext highlighter-rouge">"mqtt:broker.hivemq.com:1883"</code>.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">createConnection</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">mqttEndpoint</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">mqtt:broker.hivemq.com:1883</span><span class="dl">"</span><span class="p">;</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">create the writer group</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">connection</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MyMqttJsonPubSubConnectionDataType</span><span class="p">({</span> <span class="na">enabled</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Connection1</span><span class="dl">"</span><span class="p">,</span> <span class="na">transportProfileUri</span><span class="p">:</span> <span class="nx">Transport</span><span class="p">.</span><span class="nx">MQTT_JSON</span><span class="p">,</span> <span class="na">address</span><span class="p">:</span> <span class="p">{</span> <span class="na">url</span><span class="p">:</span> <span class="nx">mqttEndpoint</span><span class="p">,</span> <span class="p">},</span> <span class="na">writerGroups</span><span class="p">:</span> <span class="p">[</span><span class="nx">writerGroup</span><span class="p">],</span> <span class="na">readerGroups</span><span class="p">:</span> <span class="p">[]</span> <span class="p">});</span> <span class="k">return</span> <span class="nx">connection</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>The PubSubConnection requires also one dataset writer definition to describe the MQTT topic of the datastream. It also contains additional flags that shape the meta-data that will be provided in each JSON message. A dataset writer needs to be assigned to a writer group.</p> <h3 id="create-the-writer-group">create the writer group</h3> <p>The writer group contains extra parameters such as <code class="language-plaintext highlighter-rouge">messageSettings</code> and <code class="language-plaintext highlighter-rouge">transportSettings</code> that are common to all dataset writers in this group. It also contains information about the publishing interval. The <code class="language-plaintext highlighter-rouge">publishingInterval</code> indicates the rate at which the JSON message will be published to the broker.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">_</span><span class="dl">"</span><span class="s2">create the dataset writer</span><span class="dl">"</span> <span class="kd">const</span> <span class="nx">writerGroup</span> <span class="o">=</span> <span class="p">{</span> <span class="na">dataSetWriters</span><span class="p">:</span> <span class="p">[</span><span class="nx">dataSetWriter</span><span class="p">],</span> <span class="na">enabled</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">publishingInterval</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">WriterGroup1</span><span class="dl">"</span><span class="p">,</span> <span class="na">messageSettings</span><span class="p">:</span> <span class="p">{</span> <span class="na">networkMessageContentMask</span><span class="p">:</span> <span class="nx">JsonNetworkMessageContentMask</span><span class="p">.</span><span class="nx">PublisherId</span><span class="p">,</span> <span class="p">},</span> <span class="na">transportSettings</span><span class="p">:</span> <span class="p">{</span> <span class="na">requestedDeliveryGuarantee</span><span class="p">:</span> <span class="nx">BrokerTransportQualityOfService</span><span class="p">.</span><span class="nx">AtMostOnce</span><span class="p">,</span> <span class="p">},</span> <span class="p">};</span> </code></pre></div></div> <h3 id="create-the-dataset-writer">create the dataset writer</h3> <p>The data set writer contains additional message setting parameters.</p> <p>The <code class="language-plaintext highlighter-rouge">dataSetName</code> parameter indicates the name of the published data set that produces the data to be sent.</p> <p>The <code class="language-plaintext highlighter-rouge">queueName</code> parameters contains the mqtt topic we want to publish to: <code class="language-plaintext highlighter-rouge">stervfive-opcua-demo/json/data/temperature-sensor1</code> .</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">dataSetWriter</span> <span class="o">=</span> <span class="p">{</span> <span class="na">dataSetFieldContentMask</span><span class="p">:</span> <span class="nx">DataSetFieldContentMask</span><span class="p">.</span><span class="nx">None</span><span class="p">,</span> <span class="na">dataSetName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">PublishedDataSet1</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataSetWriterId</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">enabled</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">dataSetWriter1</span><span class="dl">"</span><span class="p">,</span> <span class="na">messageSettings</span><span class="p">:</span> <span class="p">{</span> <span class="na">dataSetMessageContentMask</span><span class="p">:</span> <span class="nx">JsonDataSetMessageContentMask</span><span class="p">.</span><span class="nx">DataSetWriterId</span> <span class="o">|</span> <span class="nx">JsonDataSetMessageContentMask</span><span class="p">.</span><span class="nx">MetaDataVersion</span><span class="p">,</span> <span class="p">},</span> <span class="na">transportSettings</span><span class="p">:</span> <span class="p">{</span> <span class="na">queueName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">stervfive-opcua-demo/json/data/temperature-sensor1</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">};</span> </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">dataSetName: "PublishedDataSet1"</code> tells the data set to publish the data that will be collected by the published data set named <code class="language-plaintext highlighter-rouge">PublishedDataSet1</code>, defined below.</p> <h3 id="create-the-published-dataset">create the published dataset</h3> <p>The PublishedDataSet object describes the content of a payload message and what are the corresponding OPCUA variables.</p> <p>The <code class="language-plaintext highlighter-rouge">dataSetMetaData.fields</code> array contains the list of properties exposed in the payload message.</p> <p>In our case, we have only one variable to expose. Let’s give it a property name. I choose <code class="language-plaintext highlighter-rouge">"Sensor.Temperature"</code>. We also need to specify that this is a <code class="language-plaintext highlighter-rouge">Double</code> value.</p> <p>The <code class="language-plaintext highlighter-rouge">dataSetSource.publishedData</code> array contains the same number of element as in the <code class="language-plaintext highlighter-rouge">dataSetMetaData.fields</code>.</p> <p>Each element of <code class="language-plaintext highlighter-rouge">dataSetSource.publishedData</code> is used to map the data set property to the corresponding OPC-UA variable on the server address space with the <code class="language-plaintext highlighter-rouge">attributeId</code> and <code class="language-plaintext highlighter-rouge">publishedVariable</code> parameters.</p> <p>It also indicates the suggested sampling interval in the <code class="language-plaintext highlighter-rouge">samplingIntervalHint</code> parameter.</p> <p>In our case, the nodeId of the varaible to monitor is <code class="language-plaintext highlighter-rouge">"ns=1;s=Temperature"</code></p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">createPublishedDataSet</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">publishedDataSet</span> <span class="o">=</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">PublishedDataSet1</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataSetMetaData</span><span class="p">:</span> <span class="p">{</span> <span class="na">fields</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Sensor.Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">builtInType</span><span class="p">:</span> <span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="nx">resolveNodeId</span><span class="p">(</span><span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">),</span> <span class="p">},</span> <span class="p">],</span> <span class="p">},</span> <span class="na">dataSetSource</span><span class="p">:</span> <span class="k">new</span> <span class="nx">PublishedDataItemsDataType</span><span class="p">({</span> <span class="na">publishedData</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">attributeId</span><span class="p">:</span> <span class="nx">AttributeIds</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="na">samplingIntervalHint</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="na">publishedVariable</span><span class="p">:</span> <span class="s2">`ns=1;s=Temperature`</span><span class="p">,</span> <span class="p">},</span> <span class="p">],</span> <span class="p">}),</span> <span class="p">};</span> <span class="k">return</span> <span class="nx">publishedDataSet</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <!-- ###### some additionnal imports ```javascript const { resolveNodeId, DataType, AttributeIds } = require("node-opcua"); const { DataSetFieldContentMask, JsonNetworkMessageContentMask, JsonDataSetMessageContentMask, BrokerTransportQualityOfService, PubSubConfigurationDataType, PubSubConnectionDataType, PublishedDataItemsDataType } = require("node-opcua-types"); const { installPubSub } = require("@sterfive/node-opcua-pubsub-server"); const { Transport, MyMqttJsonPubSubConnectionDataType } = require("@sterfive/node-opcua-pubsub-expander"); ``` --> <h3 id="starting-the-server">starting the server</h3> <p>We can start the server</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npx node server.js </code></pre></div></div> <h4 id="subscribing-to-the-opcua-pubsub-topic">Subscribing to the OPCUA PubSub topic</h4> <p>We can now verify that a MQTT client can subscribe to the published data. Let visit the HiveMQ’s public WebSocket client page at http://www.hivemq.com/demos/websocket-client/ .</p> <p>and add a topic subscription to our topic: <code class="language-plaintext highlighter-rouge">stervfive-opcua-demo/json/data/temperature-sensor1</code>.</p> <p><img src="/images/2022-02-16/hiveMqStream.png" alt="image" width="100%" /></p> <p>Here we go ! the data stream is up and running. It’s time to collect the flow of data to feed our data lake and produce the data we need for your predictive maintenance application.</p> <h2 id="conclusion">Conclusion</h2> <p>We have demonstrated how to add PubSUb support to a <a href="https://node-opcua.github.io">node-opcua</a> server application and created a data stream that publishes a sensor value to an MQTT broker by using a standardized OPCUA JSON payload.</p> <p>Our next article will explore the <code class="language-plaintext highlighter-rouge">PublishSubscribe</code> object in the server address space. This object exposes the PubSub configuration of the OPC-UA server. It’s used to modify the connection parameters or the payload shape; dynamically, while the server is running.</p> <p>The <code class="language-plaintext highlighter-rouge">node-opcua-pubsub-*</code> modules demonstrated here are developed by sterfive. They are published under a dual-license. The public version is suitable for evaluation in your proof of concept. The professional edition can be subscribed to from www.sterfive.com and comes with complete source code, unit tests, an extended license, professional support. It is suitable for industrial grade applications. <a href="https://www.sterfive.com/#/contact">contact us for more information</a></p> <p>references:</p> <ul> <li><a href="https://leanpub.com/node-opcuabyexample">the “Node-OPCUA by example” book</a></li> <li><a href="https://support.sterfive.com">the Node-OPCUA membership subscription</a></li> <li><a href="https://www.hivemq.com/public-mqtt-broker/">HiveMQ’s public demo MQTT broker</a></li> <li><a href="http://www.hivemq.com/demos/websocket-client/">HiveMQ public WebCient</a></li> <li><a href="https://mosquitto.org/">Eclipse Mosquitto</a></li> <li><a href="https://giphy.com/gifs/n6mEMqAuYOQ8l8qcEE">Gyphy</a></li> </ul> Wed, 16 Feb 2022 18:00:00 +0000 https://node-opcua.github.io/tutorial/2022/02/16/node-opcua-pubsub-episode1.html https://node-opcua.github.io/tutorial/2022/02/16/node-opcua-pubsub-episode1.html tutorial Node-OPCUA goes typescript <p>The incoming version of node-opcua (version 2.0.0) introduces a better support for typescript and is now nearly completed. This will make it even easier to create comprehensive server or client application within your favorite IDE.</p> <p>You can try to use it now</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir </span>node-opcua-goes-typescript <span class="nv">$ </span><span class="nb">cd </span>node-opcua-goes-typescript <span class="nv">$ </span>npm init <span class="nv">$ </span>npm <span class="nb">install </span>node-opcua@next <span class="nv">$ </span>npm dedupe </code></pre></div></div> <p>now create a sample.ts file with the following code using your favourite IDE ( I am a fan of WebStorm &amp; VSCode)</p> <div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">OPCUAClient</span><span class="p">,</span> <span class="nx">AttributeIds</span><span class="p">,</span> <span class="nx">DataValue</span><span class="p">,</span> <span class="nx">Variant</span><span class="p">,</span> <span class="nx">NodeId</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">;</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="nx">OPCUAClient</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span> <span class="na">endpoint_must_exist</span><span class="p">:</span> <span class="kc">false</span><span class="p">});</span> <span class="nx">client</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">backoff</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">retry</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">delay</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> cannot connect to endpoint retry = </span><span class="dl">"</span><span class="p">,</span> <span class="nx">retry</span><span class="p">,</span> <span class="dl">"</span><span class="s2"> next attempt in </span><span class="dl">"</span> <span class="p">,</span> <span class="nx">delay</span><span class="o">/</span><span class="mi">1000</span><span class="p">,</span> <span class="dl">"</span><span class="s2">seconds</span><span class="dl">"</span><span class="p">);</span> <span class="p">});</span> <span class="c1">// put the endpoint to your OPCUA Server here</span> <span class="kd">const</span> <span class="nx">endpointUrl</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">opc.tcp://localhost:48020</span><span class="dl">"</span><span class="p">;</span> <span class="k">try</span> <span class="p">{</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">endpointUrl</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">session</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">createSession</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">dataValue</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">session</span><span class="p">.</span><span class="nx">read</span><span class="p">({</span> <span class="na">attributeId</span><span class="p">:</span> <span class="nx">AttributeIds</span><span class="p">.</span><span class="nx">Value</span><span class="p">,</span> <span class="na">nodeId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">i=2258</span><span class="dl">"</span> <span class="c1">// Server CurrentTime</span> <span class="p">});</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Server time is </span><span class="dl">"</span><span class="p">,</span> <span class="nx">dataValue</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span> <span class="k">await</span> <span class="nx">session</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">disconnect</span><span class="p">();</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> err </span><span class="dl">"</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">main</span><span class="p">();</span> </code></pre></div></div> <p>In order to run the script you will need to use ts-node</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npx ts-node sample.ts </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">npx</code> is a utility that comes with the most recent version of nodejs and that automatically fetches ts-node if not present or not up to date on your computer, before running it.</p> <p>Alternatively, you can install ts-node permanently on your computer</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm <span class="nb">install</span> <span class="nt">-g</span> ts-node </code></pre></div></div> <p>and run your typescript program this way</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ts-node sample.ts </code></pre></div></div> <p>Do not forget the <code class="language-plaintext highlighter-rouge">await</code> keyword! as you may have unexpected behavior at runtime. However, your IDE should warn you if you forget to do something with the Promised returned by an async function.</p> <p>All asynchronous API are available either with a Promise or Callback version, you can therefore using in either context. Node-opcua automatically detects which version you’re trying to use by checking if he lastet argument is a callback function or not. This will allow you to transition smoothly from callback to async/await in your code.</p> <div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// callback version</span> <span class="kd">function</span> <span class="nx">readSomeNode1</span><span class="p">(</span><span class="nx">session</span><span class="p">:</span> <span class="nx">ClientSession</span><span class="p">,</span> <span class="nx">nodeId</span><span class="p">:</span> <span class="nx">NodeId</span><span class="p">,</span> <span class="nx">callback</span><span class="p">:(</span><span class="nx">err</span><span class="p">?:</span> <span class="nb">Error</span><span class="o">|</span><span class="kc">null</span> <span class="p">)</span> <span class="o">=&gt;</span> <span class="k">void</span><span class="p">)</span> <span class="p">{</span> <span class="nx">session</span><span class="p">.</span><span class="nx">read</span><span class="p">({</span> <span class="na">nodeId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">i=2258</span><span class="dl">"</span><span class="p">,</span> <span class="na">attributeId</span><span class="p">:</span> <span class="nx">AttributeIds</span><span class="p">.</span><span class="nx">Value</span><span class="p">},</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">err</span><span class="p">:</span> <span class="nb">Error</span><span class="o">|</span><span class="kc">null</span><span class="p">,</span> <span class="nx">dataValue</span><span class="p">?:</span> <span class="nx">DataValue</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">){</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);}</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">dataValue</span><span class="o">!</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span> <span class="nx">callback</span><span class="p">();</span> <span class="p">});</span> <span class="p">}</span> </code></pre></div></div> <div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Promise version</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">readSomeNode2</span><span class="p">(</span><span class="nx">session</span><span class="p">:</span> <span class="nx">ClientSession</span><span class="p">,</span> <span class="nx">nodeId</span><span class="p">:</span> <span class="nx">NodeId</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">dataValue</span> <span class="o">=</span> <span class="k">async</span> <span class="nx">session</span><span class="p">.</span><span class="nx">read</span><span class="p">({</span> <span class="na">nodeId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">i=2258</span><span class="dl">"</span><span class="p">,</span> <span class="na">attributeId</span><span class="p">:</span> <span class="nx">AttributeIds</span><span class="p">.</span><span class="nx">Value</span><span class="p">});</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">dataValue</span><span class="o">!</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span> <span class="p">}</span> </code></pre></div></div> <p>You will notice a few breaking changes with the previous version: Those changes are here top better isolate the public api from the private code and also to better conform to the callback. This is documented in this <a href="https://github.com/node-opcua/node-opcua/wiki/2.0.0-breaking-change-proposal">wiki page</a>.</p> <p>Typescript Inspection</p> <p>With Typescrip you will be able to easily find what function to call and what arguments is needed</p> <p>If you wonder what are the options you can pass to the OPCUAClient constructor you could press CTRL+SPACE in Webstorm to get a list of possible field. If you press CTRL+Q you can even get the associated field documentation in the right side panel.</p> <p><img src="/images/typescript_inspection2.png" alt="image" class="img-responsive" /></p> <p>This is also useful to find the various overloaded version of the same method, for instance you can discover this way all the type of eventName that you could listen to on the session object.</p> <p><img src="/images/typescript_inspection1.png" alt="image" class="img-responsive" /></p> Tue, 15 Jan 2019 11:00:00 +0000 https://node-opcua.github.io/info/2019/01/15/node-opcua-goes-typescript.html https://node-opcua.github.io/info/2019/01/15/node-opcua-goes-typescript.html info Node-Opcua 0.2.0 will make it easier than ever to write OPC-UA Client code <p><img src="/images/demo2_typescript_async.png" alt="image" class="img-responsive" /></p> <p>Since node-opcua@0.2.0, it’s possible to take advantage of the latest Javascript ES2017 feature such as <a href="">async/await</a>.</p> <p>If you are using nodejs 8 or above, most of Javascript 2017 new features are natively supported, and writing a simple OPCUA client has never been so easy.</p> <p>Let’s have a look,</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">endpointUrl</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">opc.tcp://opcuademo.sterfive.com:26543</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">opcua</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">AttributeIds</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">AttributeIds</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">OPCUAClient</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">OPCUAClient</span><span class="p">;</span> <span class="p">(</span><span class="k">async</span> <span class="kd">function</span> <span class="nx">main</span><span class="p">(){</span> <span class="kd">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OPCUAClient</span><span class="p">({});</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">endpointUrl</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">session</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">createSession</span><span class="p">({</span><span class="na">userName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">user1</span><span class="dl">"</span><span class="p">,</span> <span class="na">password</span><span class="p">:</span> <span class="dl">"</span><span class="s2">password1</span><span class="dl">"</span><span class="p">});</span> <span class="kd">const</span> <span class="nx">dataValue</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">session</span><span class="p">.</span><span class="nx">read</span><span class="p">({</span><span class="na">nodeId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ns=1;s=Temperature</span><span class="dl">"</span><span class="p">,</span><span class="na">attributeId</span><span class="p">:</span> <span class="nx">AttributeIds</span><span class="p">.</span><span class="nx">Value</span><span class="p">});</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Temperature is </span><span class="p">${</span><span class="nx">dataValue</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">toPrecision</span><span class="p">(</span><span class="mi">3</span><span class="p">)}</span><span class="s2">°C.`</span><span class="p">);</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">closeSession</span><span class="p">(</span><span class="nx">session</span><span class="p">,</span><span class="kc">true</span><span class="p">);</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nx">disconnect</span><span class="p">();</span> <span class="p">})();</span> </code></pre></div></div> <p>This simple program establishes a connection to a remote OPCUA Server, opens a session with some credentials, reads a variable value out of one of the temperature sensor exposed by the OPCUA server, print out the value, and shutdown the connection properly.</p> <p>The code looks sequential, but almost every <code class="language-plaintext highlighter-rouge">await</code> statements are in fact asynchronous calls that perform a round trip transaction with the remote server.</p> <p>This also works in TypeScript. Modern IDE such as Visual Studio Code or WebStorm provides intelisense support Typescript. This gives great user experience to explore the rich NodeOPCUA API and also statically check your code against the API signature.</p> <p>All you’ll have to do is replace those 3 line:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">opcua</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">AttributeIds</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">AttributeIds</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">OPCUAClient</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">OPCUAClient</span><span class="p">;</span> </code></pre></div></div> <p>with:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">AttributeIds</span><span class="p">,</span> <span class="nx">OPCUAClient</span><span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">;</span> </code></pre></div></div> <h3 id="wait-a-minute-the-node-version-i-am-using-doesnt-support-awaitasync-natively-">Wait a minute! the node version I am using doesn’t support await/async natively !</h3> <p>Async-await have been available since Javascript ES2017 and Node.js v8.0 or above. But, if your application still relies on older version of node (6.0 or 7.0), you can still use the Promise version, in a less convenient but still practical way.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">endpointUrl</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">opc.tcp://opcuademo.sterfive.com:26543</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">opcua</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">AttributeIds</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">AttributeIds</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">OPCUAClient</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">OPCUAClient</span><span class="p">;</span> <span class="p">(</span><span class="kd">function</span> <span class="nx">main</span><span class="p">(){</span> <span class="kd">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OPCUAClient</span><span class="p">({});</span> <span class="kd">let</span> <span class="nx">the_session</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="nx">client</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">endpointUrl</span><span class="p">)</span> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">client</span><span class="p">.</span><span class="nx">createSession</span><span class="p">({</span><span class="na">userName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">user1</span><span class="dl">"</span><span class="p">,</span> <span class="na">password</span><span class="p">:</span> <span class="dl">"</span><span class="s2">password1</span><span class="dl">"</span><span class="p">});</span> <span class="p">})</span> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">session</span><span class="p">){</span> <span class="nx">the_session</span> <span class="o">=</span> <span class="nx">session</span><span class="p">;</span> <span class="k">return</span> <span class="nx">session</span><span class="p">.</span><span class="nx">read</span><span class="p">({</span> <span class="na">nodeId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ns=1;s=Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">attributeId</span><span class="p">:</span> <span class="nx">AttributeIds</span><span class="p">.</span><span class="nx">Value</span> <span class="p">});</span> <span class="p">})</span> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">dataValue</span><span class="p">){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Temperature is </span><span class="p">${</span><span class="nx">dataValue</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">toPrecision</span><span class="p">(</span><span class="mi">3</span><span class="p">)}</span><span class="s2">°C.`</span><span class="p">);</span> <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">client</span><span class="p">.</span><span class="nx">closeSession</span><span class="p">(</span><span class="nx">the_session</span><span class="p">,</span><span class="kc">true</span><span class="p">);</span> <span class="p">}).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">client</span><span class="p">.</span><span class="nx">disconnect</span><span class="p">();</span> <span class="p">});</span> <span class="p">})();</span> </code></pre></div></div> <p>Alternatively, you could use the async/await typescript code and get it transpiled for Javascript ES6.</p> <h3 id="my-node-version-is-so-old-that-it-doesnt-support-promise-either-">my node version is so old that it doesn’t support Promise either !</h3> <p>However, if you’re still using node 4.0, native support to Promise doesn’t exist and you may have to use the old coding style , and avoid falling into <a href="http://callbackhell.com/">callback hell</a> by using the <a href="http://caolan.github.io/async/global.html">async module</a>.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">endpointUrl</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">opc.tcp://opcuademo.sterfive.com:26543</span><span class="dl">"</span><span class="p">;</span> <span class="kd">var</span> <span class="k">async</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">async</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">opcua</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">AttributeIds</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">AttributeIds</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">OPCUAClient</span> <span class="o">=</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">OPCUAClient</span><span class="p">;</span> <span class="p">(</span><span class="kd">function</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">OPCUAClient</span><span class="p">({});</span> <span class="kd">var</span> <span class="nx">the_session</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span> <span class="k">async</span><span class="p">.</span><span class="nx">series</span><span class="p">([</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="nx">client</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">endpointUrl</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span> <span class="p">},</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="nx">client</span><span class="p">.</span><span class="nx">createSession</span><span class="p">({</span><span class="na">userName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">user1</span><span class="dl">"</span><span class="p">,</span> <span class="na">password</span><span class="p">:</span> <span class="dl">"</span><span class="s2">password1</span><span class="dl">"</span><span class="p">},</span><span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">session</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="nx">the_session</span> <span class="o">=</span> <span class="nx">session</span><span class="p">;</span> <span class="nx">callback</span><span class="p">();</span> <span class="p">});</span> <span class="p">},</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">the_session</span><span class="p">.</span><span class="nx">read</span><span class="p">({</span> <span class="na">nodeId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ns=1;s=Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">attributeId</span><span class="p">:</span> <span class="nx">AttributeIds</span><span class="p">.</span><span class="nx">Value</span> <span class="p">},</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">dataValue</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Temperature is </span><span class="dl">"</span><span class="p">,</span> <span class="nx">dataValue</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">toPrecision</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="dl">"</span><span class="s2">°C.</span><span class="dl">"</span><span class="p">);</span> <span class="nx">callback</span><span class="p">();</span> <span class="p">});</span> <span class="p">},</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="nx">client</span><span class="p">.</span><span class="nx">closeSession</span><span class="p">(</span><span class="nx">the_session</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span> <span class="p">},</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">client</span><span class="p">.</span><span class="nx">disconnect</span><span class="p">(</span><span class="nx">callback</span><span class="p">);</span> <span class="p">}</span> <span class="p">],</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Err !!! </span><span class="dl">"</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span> <span class="p">})();</span> </code></pre></div></div> <p>As you can see, this code is far more verbose.</p> <p>** So ! Which one do you prefer ? **</p> <p>** Isn’t it time to switch to <a href="https://nodejs.org">nodejs 9</a> ?**</p> <h3 id="reference">reference:</h3> <ul> <li><a href="https://blog.risingstack.com/mastering-async-await-in-nodejs">mastering async-await in nodejs</a></li> <li><a href="https://tutorialzine.com/2017/07/javascript-async-await-explained">async await explained</a></li> <li><a href="http://node.green/">http://node.green/</a> provides a nice way to compare different version of nodejs and their conformance to the various evolution of the Javascript language.</li> </ul> <p>by Etienne Rossignon - Founder at <a href="https://www.sterfive.com">Sterfive</a> and <a href="https://node-opcua.github.io">NodeOPCUA</a> author</p> Mon, 22 Jan 2018 11:00:00 +0000 https://node-opcua.github.io/tutorial/2018/01/22/node-opcua-embraces-async-await.html https://node-opcua.github.io/tutorial/2018/01/22/node-opcua-embraces-async-await.html tutorial Node-OPCUA 0.0.50 has been released <h2 id="whats-new-in-nodeopcua-0050">what’s new in NodeOPCUA 0.0.50</h2> <p>This version add a significant number of enhancements. You can access the details in the <a href="https://github.com/node-opcua/node-opcua/releases/tag/v0.0.50">release notes</a></p> <h4 id="enhancements">Enhancements</h4> <ul> <li> <p>AddresseSpace helper methods have been added to create DI objects of type TwoStateDiscreteType , MultiStateDiscreteType and MultiStateValueDiscreteType</p> </li> <li> <p>AddresseSpace#addTwoStateDiscreteType</p> </li> </ul> <div class="indentedBlock"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">addressSpace</span> <span class="o">=</span> <span class="nx">engine</span><span class="p">.</span><span class="nx">addressSpace</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">rootFolder</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">findObject</span><span class="p">(</span><span class="dl">"</span><span class="s2">ObjectsFolder</span><span class="dl">"</span><span class="p">);</span> <span class="nx">rootFolder</span><span class="p">.</span><span class="nx">browseName</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Objects</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">prop</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addTwoStateDiscreteType</span><span class="p">({</span> <span class="na">organizedBy</span><span class="p">:</span> <span class="nx">rootFolder</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MySwitch</span><span class="dl">"</span><span class="p">,</span> <span class="na">trueState</span><span class="p">:</span> <span class="dl">"</span><span class="s2">busy</span><span class="dl">"</span><span class="p">,</span> <span class="na">falseState</span><span class="p">:</span> <span class="dl">"</span><span class="s2">idle</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="kc">false</span> <span class="p">});</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">browseName</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">MySwitch</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">getPropertyByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">TrueState</span><span class="dl">"</span><span class="p">).</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span> <span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Variant(Scalar&lt;LocalizedText&gt;, value: locale=null text=busy)</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">getPropertyByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">FalseState</span><span class="dl">"</span><span class="p">).</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span> <span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Variant(Scalar&lt;LocalizedText&gt;, value: locale=null text=idle)</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Variant(Scalar&lt;Boolean&gt;, value: false)</span><span class="dl">"</span><span class="p">);</span> </code></pre></figure> </div> <ul> <li>AddresseSpace#addMultiStateDiscreteType</li> </ul> <div class="indentedBlock"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="kd">var</span> <span class="nx">prop</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addMultiStateDiscreteType</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">rootFolder</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyMultiStateVariable</span><span class="dl">"</span><span class="p">,</span> <span class="na">enumStrings</span><span class="p">:</span> <span class="p">[</span> <span class="dl">"</span><span class="s2">Red</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">Orange</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">Green</span><span class="dl">"</span><span class="p">],</span> <span class="na">value</span><span class="p">:</span> <span class="mi">1</span> <span class="c1">// Orange</span> <span class="p">});</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">browseName</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">MyMultiStateVariable</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">valueRank</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">getPropertyByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">EnumStrings</span><span class="dl">"</span><span class="p">).</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span> <span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Variant(Array&lt;LocalizedText&gt;, l= 3, value=[locale=null text=Red,locale=null text=Orange,locale=null text=Green])</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">enumStrings</span><span class="p">.</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">dataType</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">LocalizedText</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Variant(Scalar&lt;UInt32&gt;, value: 1)</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">dataType</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">UInt32</span><span class="p">);</span> </code></pre></figure> </div> <p>…..see <a href="https://github.com/node-opcua/node-opcua/blob/master/test/data_access/subtest_multi_state_discrete_type.js">unit test</a></p> <ul> <li>AddresseSpace#AddMultiStateValueDiscreteType</li> </ul> <div class="indentedBlock"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">addressSpace</span> <span class="o">=</span> <span class="nx">engine</span><span class="p">.</span><span class="nx">addressSpace</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">rootFolder</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">findObject</span><span class="p">(</span><span class="dl">"</span><span class="s2">ObjectsFolder</span><span class="dl">"</span><span class="p">);</span> <span class="nx">rootFolder</span><span class="p">.</span><span class="nx">browseName</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Objects</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">prop</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addMultiStateValueDiscreteType</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">rootFolder</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyMultiStateValueVariable</span><span class="dl">"</span><span class="p">,</span> <span class="na">enumValues</span><span class="p">:</span> <span class="p">{</span> <span class="dl">"</span><span class="s2">Red</span><span class="dl">"</span><span class="p">:</span> <span class="mh">0xFF0000</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Orange</span><span class="dl">"</span><span class="p">:</span> <span class="mh">0xFF9933</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Green</span><span class="dl">"</span><span class="p">:</span> <span class="mh">0x00FF00</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Blue</span><span class="dl">"</span><span class="p">:</span> <span class="mh">0x0000FF</span> <span class="p">},</span> <span class="na">value</span><span class="p">:</span> <span class="mh">0xFF0000</span> <span class="c1">// Red</span> <span class="p">});</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">browseName</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">MyMultiStateValueVariable</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">v</span> <span class="o">=</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">getPropertyByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">EnumValues</span><span class="dl">"</span><span class="p">).</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">;</span> <span class="nx">v</span><span class="p">.</span><span class="nx">dataType</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">ExtensionObject</span><span class="p">);</span> <span class="nx">v</span><span class="p">.</span><span class="nx">arrayType</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">VariantArrayType</span><span class="p">.</span><span class="nb">Array</span><span class="p">);</span> <span class="nx">v</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">length</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="mi">4</span><span class="p">);</span> <span class="nx">v</span><span class="p">.</span><span class="nx">value</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="kd">constructor</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">EnumValueType</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Variant(Scalar&lt;UInt32&gt;, value: 16711680)</span><span class="dl">"</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">dataType</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">UInt32</span><span class="p">);</span> <span class="nx">prop</span><span class="p">.</span><span class="nx">valueAsText</span><span class="p">.</span><span class="nx">readValue</span><span class="p">().</span><span class="nx">value</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">text</span><span class="p">.</span><span class="nx">should</span><span class="p">.</span><span class="nx">eql</span><span class="p">(</span><span class="dl">"</span><span class="s2">Red</span><span class="dl">"</span><span class="p">);</span></code></pre></figure> </div> <p>see <a href="https://github.com/node-opcua/node-opcua/blob/master/test/data_access/subtest_multi_state_value_discrete_type.js">unit test</a></p> <ul> <li> <p>UAVariable#bindExtensionObject add the ability to create a Variable containing an array extension object,<br /> and exposing elements as components.</p> </li> <li> <p>Variant can now be Matrices and have dimensions</p> </li> </ul> <div class="indentedBlock"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">var1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">Variant</span><span class="p">({</span> <span class="na">dataType</span><span class="p">:</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">UInt32</span><span class="p">,</span> <span class="na">arrayType</span><span class="p">:</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">VariantArrayType</span><span class="p">.</span><span class="nx">Matrix</span><span class="p">,</span> <span class="na">dimensions</span><span class="p">:</span> <span class="p">[</span> <span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">],</span> <span class="c1">// 2 lines , 3 columns</span> <span class="na">value</span> <span class="p">:</span> <span class="p">[</span> <span class="mh">0x000</span><span class="p">,</span><span class="mh">0x001</span><span class="p">,</span><span class="mh">0x002</span><span class="p">,</span> <span class="c1">// first line</span> <span class="mh">0x010</span><span class="p">,</span><span class="mh">0x011</span><span class="p">,</span><span class="mh">0x012</span> <span class="c1">// second line</span> <span class="p">]</span> <span class="p">});</span></code></pre></figure> </div> <h4 id="breaking-changes">Breaking changes:</h4> <ul> <li> <p><code class="language-plaintext highlighter-rouge">server.engine.address_space</code> now becomes <code class="language-plaintext highlighter-rouge">server.engine.addressSpace</code> to comply with <a href="http://javascript.info/draft/variable-naming">camelCase</a> naming convention.</p> </li> <li> <p>AddressSpace#addVariable now takes a single argument. The parent object is now specified in the parameters, using the <code class="language-plaintext highlighter-rouge">propertyOf</code>, <code class="language-plaintext highlighter-rouge">componentOf</code>, or <code class="language-plaintext highlighter-rouge">organizedBy</code> attributes.</p> </li> </ul> <div class="indentedBlock"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">(</span><span class="nx">parent</span><span class="p">,{</span><span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyVar</span><span class="dl">"</span><span class="p">});</span></code></pre></figure> becomes : <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">parent</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyVar</span><span class="dl">"</span><span class="p">});</span></code></pre></figure> </div> <ul> <li>AddressSpace#addProperty has been deprecated.</li> </ul> <div class="indentedBlock"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addProperty</span><span class="p">(</span><span class="nx">parent</span><span class="p">,{</span><span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyVar</span><span class="dl">"</span><span class="p">});</span></code></pre></figure> becomes : <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">propertyOf</span><span class="p">:</span> <span class="nx">parent</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyVar</span><span class="dl">"</span><span class="p">});</span></code></pre></figure> </div> Sat, 05 Dec 2015 11:00:00 +0000 https://node-opcua.github.io/news/2015/12/05/nodejs-5.0.html https://node-opcua.github.io/news/2015/12/05/nodejs-5.0.html news Node-Opcua 0.0.50 : ObjectType and Object instantiation <h4 id="setting-parent-node-when-creating-object-and-variable-nodes">setting parent node when creating Object and Variable nodes.</h4> <p>When creating a object of variable node, you can now establish a reference link with its parent node using the <code class="language-plaintext highlighter-rouge">componentOf</code> , <code class="language-plaintext highlighter-rouge">propertyOf</code>, or <code class="language-plaintext highlighter-rouge">organizedBy</code> property of the <code class="language-plaintext highlighter-rouge">options</code> object.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">addressSpace</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addressSpace</span><span class="p">.</span> <span class="kd">var</span> <span class="nx">objectFolder</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">findObjectByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">ObjectFolder</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">myObject</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addObject</span><span class="p">({</span> <span class="na">organizedBy</span><span class="p">:</span> <span class="nx">objectFolder</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyObject</span><span class="dl">"</span> <span class="p">});</span> <span class="kd">var</span> <span class="nx">myVar</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addVariale</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">myObject</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="k">new</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">Variant</span><span class="p">({</span> <span class="na">dataType</span><span class="p">:</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="mf">10.0</span><span class="p">});</span> <span class="p">});</span></code></pre></figure> <h3 id="creating-a-new-objecttype">creating a new ObjectType</h3> <p>You can easily declare a new object type in your addressSpace. ObjectType are useful to help clients of your server to detect easily if the address space of the server contains objects of the given type.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">temperatureSensorType</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addObjectType</span><span class="p">({</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">TemperatureSensorType</span><span class="dl">"</span> <span class="p">});</span></code></pre></figure> <p>By default, newly created type are sub type of “BaseObjectType”, unless you specify a “subTypeOf” property in the <code class="language-plaintext highlighter-rouge">options</code> object. For instance, we could derive a new ObjectType from our TemperatureSensorType.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">specialTemperatureSensorType</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addObjectType</span><span class="p">({</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">specialTemperatureSensorType</span><span class="dl">"</span><span class="p">,</span> <span class="na">subTypeOf</span><span class="p">:</span> <span class="nx">temperatureSensorType</span> <span class="p">});</span></code></pre></figure> <p>Note: the subTypeOf property could be given either a Node object, a nodeId object, or even a string matching the browsename of the node you wish to refer to.</p> <h3 id="enriching-an-objecttype-with-components-and-properties">enriching an ObjectType with components and properties</h3> <p>ObjetType are useful to provide a description of what components or properties are expected on object instance of this type.</p> <p>For instance, we could specify that objects of type TemperatureSensorType are expected to have a mandatory “Temperature” component and a optional “IdentificationNumber” component.</p> <p>The “Temperature” and “IdentificationNumber” variables will be linked with the main temperatureSensor instance with an <code class="language-plaintext highlighter-rouge">"HasComponent"</code> reference.</p> <p>We can specify <code class="language-plaintext highlighter-rouge">modellingRule</code> as <code class="language-plaintext highlighter-rouge">"Mandatory"</code>, <code class="language-plaintext highlighter-rouge">"Optional"</code> , <code class="language-plaintext highlighter-rouge">"PlaceHolderMandatory"</code>, <code class="language-plaintext highlighter-rouge">"PlaceHolderOptional"</code>. If the <code class="language-plaintext highlighter-rouge">modellingRule</code> is missing, <code class="language-plaintext highlighter-rouge">"Mandatory"</code> is assumed.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">temperatureSensorType</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addObjectType</span><span class="p">({</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">TemperatureSensorType</span><span class="dl">"</span> <span class="p">});</span> <span class="kd">var</span> <span class="nx">temperature</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">temperateurSensorType</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">the temperature value (in degree C) measured by the sensor</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Float</span><span class="dl">"</span><span class="p">,</span> <span class="na">modellingRule</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Mandatory</span><span class="dl">"</span> <span class="p">});</span> <span class="kd">var</span> <span class="nx">idNumber</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">temperateurSensorType</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">IdentificationNumber</span><span class="dl">"</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">the identification number of the sensor (could be serial number of 1Wire ID)</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">String</span><span class="dl">"</span><span class="p">,</span> <span class="na">modellingRule</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Optional</span><span class="dl">"</span> <span class="p">});</span></code></pre></figure> <h3 id="instantiating-an-object-node-from-its-objecttype">instantiating an Object node from its ObjectType.</h3> <p>A instance of our TemperatureSensor Type can now be easily created.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">temperatureSensor</span> <span class="o">=</span> <span class="nx">temperatureSensorType</span><span class="p">.</span><span class="nx">instantiate</span><span class="p">({</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyTemperatureSensor</span><span class="dl">"</span> <span class="p">});</span></code></pre></figure> <p>This newly created instance is already fitted with fresh instances of the mandatory components and propertiesthat are defined in the ObjectType. The temperatureSensor node created above has already a temperature component that can be set with a a value.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="nx">temperatureSensor</span><span class="p">.</span><span class="nx">getComponentByName</span><span class="p">(</span><span class="dl">"</span><span class="s2">Temperature</span><span class="dl">"</span><span class="p">).</span><span class="nx">setValueFromSource</span><span class="p">({</span> <span class="na">dataType</span><span class="p">:</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="mf">37.2</span> <span class="p">});</span></code></pre></figure> <p>Conveniently, NodeOPCUA has automatically added new members to the temperatureSensor object so you can access OPCUA properties and components more easily.</p> <p>The code above could be written this way:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="nx">temperatureSensor</span><span class="p">.</span><span class="nx">temperature</span><span class="p">.</span><span class="nx">setValueFromSource</span><span class="p">({</span> <span class="na">dataType</span><span class="p">:</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="mf">37.2</span> <span class="p">});</span></code></pre></figure> <p>Note that the name of the javascript property doesn’t match exactly the nameof the UA component of Property. In javacript, we follow the camel case naming convention. For this reason, the “Temperature” component is accessible throught the javascript “temperature” (starting with a lower case ‘t’ ).</p> <p>By default, ObjectType#instantiate only instantiate components and properties that have a “Mandatory” modelling rule. Component or property marked as optional in the ObjectType definition will be instatiated if their browseName appear in the <code class="language-plaintext highlighter-rouge">options.optional</code> array.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">temperatureSensor</span> <span class="o">=</span> <span class="nx">temperatureSensorType</span><span class="p">.</span><span class="nx">instantiate</span><span class="p">({</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">MyTemperatureSensor</span><span class="dl">"</span><span class="p">,</span> <span class="na">optional</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">IdentificationNumber</span><span class="dl">"</span><span class="p">]</span> <span class="p">});</span> <span class="nx">temperatureSensor</span><span class="p">.</span><span class="nx">identificationNumber</span><span class="p">.</span><span class="nx">setValueFromSource</span><span class="p">({</span> <span class="na">dataType</span><span class="p">:</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nb">String</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="dl">"</span><span class="s2">SN#1234</span><span class="dl">"</span> <span class="p">});</span></code></pre></figure> Sat, 05 Dec 2015 11:00:00 +0000 https://node-opcua.github.io/tutorial/2015/12/05/ObjectType-and-object-instantiation.html https://node-opcua.github.io/tutorial/2015/12/05/ObjectType-and-object-instantiation.html tutorial weather station in node-opcua <h1 id="creating-a-opc-ua-server-for-a-virtual-weather-station">Creating a OPC-UA server for a virtual weather station.</h1> <p>update on 26/06/2016 for node-opcua 0.0.55.</p> <h2 id="purpose">Purpose</h2> <p>In my quest of exploring the <a href="http://en.wikipedia.org/wiki/Internet_of_Things">“Internet of Things”</a> world, I decided to create a simple weather station with 3 sensors mounted on my Raspberry computer. I needed to buy some equipment to build the prototype. After studying different type of sensors (1-Wire,Analog,I2C), I finally opted for I2C sensors. (I used I2C chips a long time ago, in a Junior Enterprise Project). I ordered a <a href="https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/robot/breadboard">breadboard</a> and <a href="http://www.ebay.com/itm/BMP085-IIC-I2C-Barometric-Pressure-module-for-AVR-Arduino-/121233041012?ssPageName=ADME:L:OU:FR:3160">I2C temperature and humidity sensor</a>.</p> <p>While waiting for the equipment to be delivered, I though it was time to start coding the Server Application.</p> <p>I got the idea of using a free Web-Service to get some realtime temperature and pressure information that I need to simulate the data.</p> <p>The server is written in Javascript, using <a href="http://www.nodejs.org">NodeJS</a>.</p> <h2 id="retrieving-weather-data-using-a-rest-api">retrieving Weather data using a REST API.</h2> <p>The virtual weather station need to extract the weather data from a web service. <a href="http://www.worldweatheronline.com/api">worldweatheronline</a> provides a free API. The <a href="http://www.worldweatheronline.com/api/docs/">Free API</a> comes with some restrictions, though, and only allows each user to make up to a maximum of 250 requests per day, a.k.a 1 request every 6 minutes)</p> <h3 id="getting-a-key-at-worldweatheronline">getting a key at worldweatheronline</h3> <p>You will need to register to <a href="https://developer.worldweatheronline.com/auth/register">worldweatheronline</a> to obtain your API key. Store you API key in a file name <code class="language-plaintext highlighter-rouge">worldweatheronline.key</code>, in your project folder.</p> <h3 id="testing-the-api">testing the API</h3> <p>The API is documented <a href="http://developer.worldweatheronline.com/documentation">here</a>.</p> <p>For example, typing the following URL in the address bar of your web browser.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http://api.worldweatheronline.com/free/v2/weather.ashx?q=London&amp;format=json&amp;key=&lt;YOURAPIKEY&gt; </code></pre></div></div> <p>will return the following JSON data</p> <figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w"> </span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"current_condition"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"cloudcover"</span><span class="p">:</span><span class="w"> </span><span class="s2">"75"</span><span class="p">,</span><span class="w"> </span><span class="nl">"humidity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"67"</span><span class="p">,</span><span class="w"> </span><span class="nl">"observation_time"</span><span class="p">:</span><span class="w"> </span><span class="s2">"11:54 AM"</span><span class="p">,</span><span class="w"> </span><span class="nl">"precipMM"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.3"</span><span class="p">,</span><span class="w"> </span><span class="nl">"pressure"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1003"</span><span class="p">,</span><span class="w"> </span><span class="nl">"temp_C"</span><span class="p">:</span><span class="w"> </span><span class="s2">"12"</span><span class="p">,</span><span class="w"> </span><span class="nl">"temp_F"</span><span class="p">:</span><span class="w"> </span><span class="s2">"54"</span><span class="p">,</span><span class="w"> </span><span class="nl">"visibility"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10"</span><span class="p">,</span><span class="w"> </span><span class="nl">"weatherCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"116"</span><span class="p">,</span><span class="w"> </span><span class="nl">"weatherDesc"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Partly Cloudy"</span><span class="w"> </span><span class="p">}],</span><span class="w"> </span><span class="nl">"weatherIconUrl"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http:</span><span class="se">\/\/</span><span class="s2">cdn.worldweatheronline.net</span><span class="se">\/</span><span class="s2">images</span><span class="se">\/</span><span class="s2">wsymbols01_png_64</span><span class="se">\/</span><span class="s2">wsymbol_0002_sunny_intervals.png"</span><span class="w"> </span><span class="p">}],</span><span class="w"> </span><span class="nl">"winddir16Point"</span><span class="p">:</span><span class="w"> </span><span class="s2">"W"</span><span class="p">,</span><span class="w"> </span><span class="nl">"winddirDegree"</span><span class="p">:</span><span class="w"> </span><span class="s2">"260"</span><span class="p">,</span><span class="w"> </span><span class="nl">"windspeedKmph"</span><span class="p">:</span><span class="w"> </span><span class="s2">"31"</span><span class="p">,</span><span class="w"> </span><span class="nl">"windspeedMiles"</span><span class="p">:</span><span class="w"> </span><span class="s2">"19"</span><span class="w"> </span><span class="p">}],</span><span class="w"> </span><span class="nl">"request"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"query"</span><span class="p">:</span><span class="w"> </span><span class="s2">"London, United Kingdom"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"City"</span><span class="w"> </span><span class="p">}],</span><span class="w"> </span><span class="nl">"weather"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2014-05-11"</span><span class="p">,</span><span class="w"> </span><span class="nl">"precipMM"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.5"</span><span class="p">,</span><span class="w"> </span><span class="nl">"tempMaxC"</span><span class="p">:</span><span class="w"> </span><span class="s2">"13"</span><span class="p">,</span><span class="w"> </span><span class="nl">"tempMaxF"</span><span class="p">:</span><span class="w"> </span><span class="s2">"56"</span><span class="p">,</span><span class="w"> </span><span class="nl">"tempMinC"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5"</span><span class="p">,</span><span class="w"> </span><span class="nl">"tempMinF"</span><span class="p">:</span><span class="w"> </span><span class="s2">"41"</span><span class="p">,</span><span class="w"> </span><span class="nl">"weatherCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"266"</span><span class="p">,</span><span class="w"> </span><span class="nl">"weatherDesc"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Light drizzle"</span><span class="w"> </span><span class="p">}],</span><span class="w"> </span><span class="nl">"weatherIconUrl"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http:</span><span class="se">\/\/</span><span class="s2">cdn.worldweatheronline.net</span><span class="se">\/</span><span class="s2">images</span><span class="se">\/</span><span class="s2">wsymbols01_png_64</span><span class="se">\/</span><span class="s2">wsymbol_0017_cloudy_with_light_rain.png"</span><span class="w"> </span><span class="p">}],</span><span class="w"> </span><span class="nl">"winddir16Point"</span><span class="p">:</span><span class="w"> </span><span class="s2">"W"</span><span class="p">,</span><span class="w"> </span><span class="nl">"winddirDegree"</span><span class="p">:</span><span class="w"> </span><span class="s2">"267"</span><span class="p">,</span><span class="w"> </span><span class="nl">"winddirection"</span><span class="p">:</span><span class="w"> </span><span class="s2">"W"</span><span class="p">,</span><span class="w"> </span><span class="nl">"windspeedKmph"</span><span class="p">:</span><span class="w"> </span><span class="s2">"33"</span><span class="p">,</span><span class="w"> </span><span class="nl">"windspeedMiles"</span><span class="p">:</span><span class="w"> </span><span class="s2">"20"</span><span class="w"> </span><span class="p">}]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span></code></pre></figure> <h2 id="reading-weather-data">Reading weather data</h2> <p>It is now time to code a function to extract the temperate, the pressure, and the humidity using nodeJs.</p> <h3 id="preparing-the-project">preparing the project</h3> <p>First of all, let create the ‘'’package.json’’’ file for our project.</p> <figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">mkdir </span>myweatherstation <span class="nb">cd </span>myweatherstation npm init</code></pre></figure> <p>While we are here, let’s install some of the npm modules that we need.</p> <figure class="highlight"><pre><code class="language-sh" data-lang="sh">npm <span class="nb">install </span>request <span class="nt">--save</span> npm <span class="nb">install </span>node-opcua <span class="nt">--save</span></code></pre></figure> <h3 id="accessing-the-worldweatheronline-api-key">accessing the worldweatheronline API key</h3> <p>Our application will need to access our API developer key. Let’s put it in a file named <code class="language-plaintext highlighter-rouge">worldweatheronline.key</code> in our project folder. The key value can be easily read in nodejs using this code.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// read the World Weather Online API key.</span> <span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">key</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">worldweatheronline.key</span><span class="dl">"</span><span class="p">);</span></code></pre></figure> <p>Lets write a small <a href="#testing-the-rest-api" title="save:">worldweather_demo.js</a>, to experiment the api.</p> <h2 id="testing-the-rest-api">testing the rest api</h2> <p>Our purpose is to create a <code class="language-plaintext highlighter-rouge">getCityWeather</code> asynchronous function that pass to a callback function an object containing the temperature and pressure of a city. This function will be used this way:</p> <!-- compile with literate-programming create_a_weather_station.md !--> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="cm">/*global require,console */</span> <span class="cm">/*jshint evil:true */</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">get city weather</span><span class="dl">"</span> <span class="kd">var</span> <span class="nx">city</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">London</span><span class="dl">"</span><span class="p">;</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="nx">city</span><span class="p">,</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">data = data</span><span class="dl">"</span><span class="p">,</span><span class="nx">data</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> city =</span><span class="dl">"</span><span class="p">,</span><span class="nx">city</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> time =</span><span class="dl">"</span><span class="p">,</span><span class="nx">data</span><span class="p">.</span><span class="nx">observation_time</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> temperature =</span><span class="dl">"</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">temperature</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> pressure =</span><span class="dl">"</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">pressure</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span></code></pre></figure> <h2 id="get-city-weather">get city weather</h2> <p>Let’s write the method that reads the weather of a city.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">_</span><span class="dl">"</span><span class="s2">accessing the worldweatheronline API key</span><span class="dl">"</span> <span class="kd">function</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="nx">city</span><span class="p">,</span><span class="nx">callback</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">api_url</span><span class="o">=</span><span class="dl">"</span><span class="s2">http://api.worldweatheronline.com/free/v2/weather.ashx?q=</span><span class="dl">"</span><span class="o">+</span><span class="nx">city</span><span class="o">+</span><span class="dl">"</span><span class="s2">+&amp;format=json&amp;key=</span><span class="dl">"</span><span class="o">+</span> <span class="nx">key</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span> <span class="na">url</span><span class="p">:</span> <span class="nx">api_url</span><span class="p">,</span> <span class="dl">"</span><span class="s2">content-type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">application-json</span><span class="dl">"</span><span class="p">,</span> <span class="na">json</span><span class="p">:</span> <span class="dl">""</span> <span class="p">};</span> <span class="kd">var</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">request</span><span class="dl">"</span><span class="p">);</span> <span class="nx">request</span><span class="p">(</span><span class="nx">options</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">response</span><span class="p">,</span> <span class="nx">body</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">error</span> <span class="o">&amp;&amp;</span> <span class="nx">response</span><span class="p">.</span><span class="nx">statusCode</span> <span class="o">===</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">perform_read</span><span class="p">(</span><span class="nx">city</span><span class="p">,</span><span class="nx">body</span><span class="p">);</span> <span class="nx">callback</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span><span class="nx">data</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span> <span class="p">}</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">extract useful data</span><span class="dl">"</span></code></pre></figure> <h3 id="extract-useful-data">extract useful data</h3> <p>The <code class="language-plaintext highlighter-rouge">perform_read</code> function convert the raw json data retrieved from the API,into a simpler Javascript object for our application.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">perform_read</span><span class="p">(</span><span class="nx">city</span><span class="p">,</span><span class="nx">body</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">body</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">current_condition</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">current_condition</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="kd">var</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">request</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="k">return</span> <span class="p">{</span> <span class="na">city</span><span class="p">:</span> <span class="nx">request</span><span class="p">.</span><span class="nx">query</span><span class="p">,</span> <span class="na">date</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(),</span> <span class="na">observation_time</span><span class="p">:</span> <span class="nx">current_condition</span><span class="p">.</span><span class="nx">observation_time</span><span class="p">,</span> <span class="na">temperature</span><span class="p">:</span> <span class="nb">parseFloat</span><span class="p">(</span><span class="nx">current_condition</span><span class="p">.</span><span class="nx">temp_C</span><span class="p">),</span> <span class="na">humidity</span><span class="p">:</span> <span class="nb">parseFloat</span><span class="p">(</span><span class="nx">current_condition</span><span class="p">.</span><span class="nx">humidity</span><span class="p">),</span> <span class="na">pressure</span><span class="p">:</span> <span class="nb">parseFloat</span><span class="p">(</span><span class="nx">current_condition</span><span class="p">.</span><span class="nx">pressure</span><span class="p">),</span> <span class="na">weather</span><span class="p">:</span> <span class="nx">current_condition</span><span class="p">.</span><span class="nx">weatherDesc</span><span class="p">.</span><span class="nx">value</span> <span class="p">};</span> <span class="p">}</span></code></pre></figure> <h3 id="reading-data-periodically">reading data periodically</h3> <p>The Weather Station Server will have to query the weather data of a city on a regular basis. However, we have to be careful not to send too many queries to the web-server, as any exceeding requests will get rejected and will lead to a error:</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;h1&gt;</span>Developer Over Rate<span class="nt">&lt;/h1&gt;</span></code></pre></figure> <p>In NodeJs, the setInterval function can be used to perform a action periodically.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">london_data</span> <span class="o">=</span> <span class="p">{}</span> <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="dl">"</span><span class="s2">London</span><span class="dl">"</span><span class="p">,</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">london_data</span> <span class="o">=</span> <span class="nx">data</span><span class="p">;</span> <span class="p">}</span> <span class="p">});</span> <span class="p">},</span> <span class="mi">60</span><span class="o">*</span><span class="mi">1000</span><span class="p">);</span></code></pre></figure> <p>Lets edit <a href="#making-a-round-robin-read" title="save:">worldweather_demo2.js</a> to experiment this.</p> <h2 id="making-a-round-robin-read">making a round robin read</h2> <p>Why not make our server expose the weather variables of more than one city ? Supposing we hold an array containing the city we want to monitor, our periodic call to the REST API will have to query the data for each city in turn. We will store the most up to date weather data inside a map <code class="language-plaintext highlighter-rouge">city_data_map</code> .</p> <p>I chose 10 cities spread in different continents and hemisphere.</p> <p>Just to make it fun, I added Longyearbyen, the <a href="http://en.wikipedia.org/wiki/Northernmost_cities_and_towns">northenmost city in the world</a> and ‘Ushuaia’ one of the <a href="http://en.wikipedia.org/wiki/Southernmost_cities_and_towns">southernmost city</a>.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="cm">/*global require,setInterval,console */</span> <span class="kd">var</span> <span class="nx">cities</span> <span class="o">=</span> <span class="p">[</span> <span class="dl">'</span><span class="s1">London</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Paris</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">New York</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Moscow</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Ho chi min</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Benjing</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Reykjavik</span><span class="dl">'</span> <span class="p">,</span><span class="dl">'</span><span class="s1">Nouakchott</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Ushuaia</span><span class="dl">'</span> <span class="p">,</span><span class="dl">'</span><span class="s1">Longyearbyen</span><span class="dl">'</span><span class="p">];</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">get city weather</span><span class="dl">"</span> <span class="kd">var</span> <span class="nx">city_data_map</span> <span class="o">=</span> <span class="p">{</span> <span class="p">};</span> <span class="c1">// a infinite round-robin iterator over the city array</span> <span class="kd">var</span> <span class="nx">next_city</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">arr</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">counter</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="k">return</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">counter</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="nx">counter</span><span class="o">&gt;=</span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span> <span class="nx">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">counter</span><span class="p">];</span> <span class="p">};</span> <span class="p">}(</span><span class="nx">cities</span><span class="p">);</span> <span class="kd">function</span> <span class="nx">update_city_data</span><span class="p">(</span><span class="nx">city</span><span class="p">)</span> <span class="p">{</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="nx">city</span><span class="p">,</span><span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">city_data_map</span><span class="p">[</span><span class="nx">city</span><span class="p">]</span> <span class="o">=</span> <span class="nx">data</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">city</span><span class="p">,</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span><span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">));</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">error city</span><span class="dl">"</span><span class="p">,</span><span class="nx">city</span> <span class="p">,</span> <span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span> <span class="p">}</span> <span class="c1">// make a API call every 10 seconds</span> <span class="kd">var</span> <span class="nx">interval</span> <span class="o">=</span> <span class="mi">10</span><span class="o">*</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">city</span> <span class="o">=</span> <span class="nx">next_city</span><span class="p">();</span> <span class="nx">update_city_data</span><span class="p">(</span><span class="nx">city</span><span class="p">);</span> <span class="p">},</span> <span class="nx">interval</span><span class="p">);</span></code></pre></figure> <h3 id="weather-server-skeleton">Weather Server Skeleton</h3> <p>It is now time to create the skeleton of our weather server.</p> <p><a href="#Weather-Server-Skeleton" title="save:">weather.js</a></p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="cm">/*global require,console,setInterval */</span> <span class="nb">Error</span><span class="p">.</span><span class="nx">stackTraceLimit</span> <span class="o">=</span> <span class="kc">Infinity</span><span class="p">;</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">making a round robin read</span><span class="dl">"</span> <span class="kd">var</span> <span class="nx">opcua</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">server</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">OPCUAServer</span><span class="p">({</span> <span class="na">port</span><span class="p">:</span> <span class="mi">4334</span> <span class="c1">// the port of the listening socket of the server</span> <span class="p">});</span> <span class="nx">server</span><span class="p">.</span><span class="nx">buildInfo</span><span class="p">.</span><span class="nx">productName</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">WeatherStation</span><span class="dl">"</span><span class="p">;</span> <span class="nx">server</span><span class="p">.</span><span class="nx">buildInfo</span><span class="p">.</span><span class="nx">buildNumber</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">7658</span><span class="dl">"</span><span class="p">;</span> <span class="nx">server</span><span class="p">.</span><span class="nx">buildInfo</span><span class="p">.</span><span class="nx">buildDate</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="mi">2014</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">2</span><span class="p">);</span> <span class="kd">function</span> <span class="nx">post_initialize</span><span class="p">()</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">initialized</span><span class="dl">"</span><span class="p">);</span> <span class="kd">function</span> <span class="nx">construct_my_address_space</span><span class="p">(</span><span class="nx">server</span><span class="p">)</span> <span class="p">{</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">construct the address space</span><span class="dl">"</span> <span class="p">}</span> <span class="nx">construct_my_address_space</span><span class="p">(</span><span class="nx">server</span><span class="p">);</span> <span class="nx">server</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Server is now listening ... ( press CTRL+C to stop)</span><span class="dl">"</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">port </span><span class="dl">"</span><span class="p">,</span> <span class="nx">server</span><span class="p">.</span><span class="nx">endpoints</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">port</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">endpointUrl</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">endpoints</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">endpointDescriptions</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">endpointUrl</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> the primary server endpoint url is </span><span class="dl">"</span><span class="p">,</span> <span class="nx">endpointUrl</span> <span class="p">);</span> <span class="p">});</span> <span class="p">}</span> <span class="nx">server</span><span class="p">.</span><span class="nx">initialize</span><span class="p">(</span><span class="nx">post_initialize</span><span class="p">);</span></code></pre></figure> <h4 id="construct-the-address-space">construct the address space</h4> <p>The server address space will be made of a <code class="language-plaintext highlighter-rouge">Cities</code> folder containing one folder for each city.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// declare some folders</span> <span class="kd">var</span> <span class="nx">cityFolder</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addFolder</span><span class="p">(</span><span class="dl">"</span><span class="s2">ObjectsFolder</span><span class="dl">"</span><span class="p">,{</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Cities</span><span class="dl">"</span><span class="p">});</span> <span class="kd">function</span> <span class="nx">create_CityNode</span><span class="p">(</span><span class="nx">city_name</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// declare the city node</span> <span class="kd">var</span> <span class="nx">cityNode</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addFolder</span><span class="p">(</span><span class="nx">cityFolder</span><span class="p">,{</span> <span class="na">browseName</span><span class="p">:</span> <span class="nx">city_name</span> <span class="p">});</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">construct city weather variables</span><span class="dl">"</span> <span class="p">}</span> <span class="nx">cities</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">city</span><span class="p">)</span> <span class="p">{</span> <span class="nx">create_CityNode</span><span class="p">(</span><span class="nx">city</span><span class="p">);</span> <span class="p">});</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">extracting a DataValue</span><span class="dl">"</span></code></pre></figure> <h4 id="extracting-a-datavalue">extracting a DataValue</h4> <p>Let’s write a helper function (<code class="language-plaintext highlighter-rouge">extract_value</code>) to extract a city weather variable as DataValue. Since the city weather data are read asynchronously at a very low rate, it is possible that the data doesn’t exist yet when the client will send its request. We have to be careful to handle this case appropriately. In the absence of city data, I have chose to send a BadUncertainInitalValue status code.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">city_name</span><span class="p">,</span><span class="nx">property</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">city</span> <span class="o">=</span> <span class="nx">city_data_map</span><span class="p">[</span><span class="nx">city_name</span><span class="p">];</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">city</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">StatusCodes</span><span class="p">.</span><span class="nx">BadDataUnavailable</span> <span class="p">}</span> <span class="kd">var</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">city</span><span class="p">[</span><span class="nx">property</span><span class="p">];</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">Variant</span><span class="p">({</span><span class="na">dataType</span><span class="p">:</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="nx">value</span> <span class="p">});</span> <span class="p">}</span></code></pre></figure> <h4 id="construct-city-weather-variables">construct city weather variables</h4> <p>Each city node exposes 3 read-only variables that can be instantiated this way:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">cityNode</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">city_name</span><span class="p">,</span><span class="dl">"</span><span class="s2">temperature</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">cityNode</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Humidity</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">city_name</span><span class="p">,</span><span class="dl">"</span><span class="s2">humidity</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">cityNode</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Pressure</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">city_name</span><span class="p">,</span><span class="dl">"</span><span class="s2">pressure</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span></code></pre></figure> <h3 id="testing-the-server">testing the server</h3> <p>It is now time to start the server for testing.</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash">node weather.js</code></pre></figure> <p>Putting everything together, the <code class="language-plaintext highlighter-rouge">weather.js</code> script looks like <a href="https://raw.githubusercontent.com/node-opcua/node-opcua/master/documentation/weather.js">this</a></p> Sun, 05 Jul 2015 11:00:00 +0000 https://node-opcua.github.io/tutorial/2015/07/05/weather-stationold.html https://node-opcua.github.io/tutorial/2015/07/05/weather-stationold.html tutorial a weather station server in Node-OPCUA <h1 id="creating-an-opc-ua-server-for-a-virtual-weather-station">Creating an OPC-UA server for a virtual weather station.</h1> <p>update on 26/06/2016 for node-opcua 0.0.55. update on 26/07/2019 for node-opcua 2.1.0.</p> <h2 id="purpose">Purpose</h2> <p>In my quest of exploring the <a href="http://en.wikipedia.org/wiki/Internet_of_Things">“Internet of Things”</a> world, I decided to create a simple weather station with three sensors mounted on my Raspberry computer. I needed to buy some equipment to build the prototype. After studying different types of sensors (1-Wire ,Analog,I2C), I finally opted for I2C sensors. (I used I2C chips a long time ago, in a Junior Enterprise Project). I ordered a <a href="https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/robot/breadboard">breadboard</a> and <a href="http://www.ebay.com/itm/BMP085-IIC-I2C-Barometric-Pressure-module-for-AVR-Arduino-/121233041012?ssPageName=ADME:L:OU:FR:3160">I2C temperature and humidity sensor</a>.</p> <p>While waiting for the equipment to be delivered, I thought it was time to start coding the Server Application.</p> <p>I got the idea of using a free Web-Service to get some real-time temperature and pressure information that I need to simulate the data.</p> <p>The server is written in TypeScript, using <a href="http://www.nodejs.org">NodeJS</a>.</p> <h2 id="retrieving-weather-data-using-a-rest-api">retrieving Weather data using a REST API.</h2> <p>The virtual weather station needs to extract the weather data from a web service. <a href="https://rapidapi.com/community/api/open-weather-map">OpenWeatherMAP</a> provides a free API.</p> <h3 id="getting-a-key-at-open-weather-map">getting a key at open-weather-map</h3> <p>You will need to register to <a href="https://rapidapi.com">RapidAPI.com</a> to obtain your API key. Store your API key in a file name <code class="language-plaintext highlighter-rouge">open-weather-map.key</code>, in your project folder.</p> <h3 id="testing-the-api">testing the API</h3> <p>The API is documented <a href="https://rapidapi.com/community/api/open-weather-map">here</a>.</p> <p>For example, type the following URL in the address bar of your web browser.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl --get --include 'https://community-open-weather-map.p.rapidapi.com/weather?callback=test&amp;id=2172797&amp;units=%22metric%22+or+%22imperial%22&amp;mode=xml%2C+html&amp;q=London%2Cuk' \ -H 'X-RapidAPI-Host: community-open-weather-map.p.rapidapi.com' \ -H 'X-RapidAPI-Key: &lt;put your key here&gt;' </code></pre></div></div> <p>will return the following JSON data</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"coord"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"lon"</span><span class="p">:</span><span class="mf">-0.13</span><span class="w"> </span><span class="nl">"lat"</span><span class="p">:</span><span class="mf">51.51</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nl">"sys"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"message"</span><span class="p">:</span><span class="mf">0.0223</span><span class="w"> </span><span class="nl">"country"</span><span class="p">:</span><span class="s2">"GB"</span><span class="w"> </span><span class="nl">"sunrise"</span><span class="p">:</span><span class="mi">1398055845</span><span class="w"> </span><span class="nl">"sunset"</span><span class="p">:</span><span class="mi">1398107249</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nl">"weather"</span><span class="p">:[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="mi">501</span><span class="w"> </span><span class="nl">"main"</span><span class="p">:</span><span class="s2">"Rain"</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="s2">"moderate rain"</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="s2">"10d"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="nl">"base"</span><span class="p">:</span><span class="s2">"cmc stations"</span><span class="w"> </span><span class="nl">"main"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"temp"</span><span class="p">:</span><span class="mf">290.04</span><span class="w"> </span><span class="nl">"humidity"</span><span class="p">:</span><span class="mi">70</span><span class="w"> </span><span class="nl">"pressure"</span><span class="p">:</span><span class="mi">1003</span><span class="w"> </span><span class="nl">"temp_min"</span><span class="p">:</span><span class="mf">287.04</span><span class="w"> </span><span class="nl">"temp_max"</span><span class="p">:</span><span class="mf">293.15</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nl">"wind"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"speed"</span><span class="p">:</span><span class="mf">0.51</span><span class="w"> </span><span class="nl">"gust"</span><span class="p">:</span><span class="mf">3.6</span><span class="w"> </span><span class="nl">"deg"</span><span class="p">:</span><span class="mi">93</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nl">"rain"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"1h"</span><span class="p">:</span><span class="mf">1.02</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nl">"clouds"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"all"</span><span class="p">:</span><span class="mi">56</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nl">"dt"</span><span class="p">:</span><span class="mi">1398100214</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="mi">2643743</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"London"</span><span class="w"> </span><span class="nl">"cod"</span><span class="p">:</span><span class="mi">200</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <h2 id="reading-weather-data">Reading weather data</h2> <p>It is now time to code a function to extract the temperate, the pressure, and the humidity using nodeJs.</p> <h3 id="preparing-the-project">preparing the project</h3> <p>First of all, let create the ‘'’package.json’’’ file for our project.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>myweatherstation <span class="nb">cd </span>myweatherstation npm init </code></pre></div></div> <p>While we are here, let’s install some of the npm modules that we need.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install </span>unirest <span class="nt">--save</span> npm <span class="nb">install </span>node-opcua <span class="nt">--save</span> </code></pre></div></div> <h3 id="accessing-the-openweathermap-api-key">accessing the openweathermap API key</h3> <p>Our application will need to access our API developer key. Let’s put it in a file named <code class="language-plaintext highlighter-rouge">openweathermap.key</code> in our project folder. The key value can be easily read in nodejs using this code.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">openweathermap.key</span><span class="dl">"</span><span class="p">);</span> </code></pre></div></div> <p>Lets write a small <a href="#testing-the-rest-api" title="save:">worldweather_demo.js</a>, to experiment the api.</p> <h2 id="testing-the-rest-api">testing the rest api</h2> <p>Our purpose is to create a <code class="language-plaintext highlighter-rouge">getCityWeather</code> asynchronous function that pass to a callback function an object containing the temperature and pressure of a city. This function will be used this way:</p> <!-- compile with literate-programming create_a_weather_station.md !--> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">_</span><span class="dl">"</span><span class="s2">get city weather</span><span class="dl">"</span> <span class="kd">const</span> <span class="nx">city</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">London</span><span class="dl">"</span><span class="p">;</span> <span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="nx">city</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">data = data</span><span class="dl">"</span><span class="p">,</span><span class="nx">data</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> city =</span><span class="dl">"</span><span class="p">,</span><span class="nx">city</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> time =</span><span class="dl">"</span><span class="p">,</span><span class="nx">data</span><span class="p">.</span><span class="nx">dt</span><span class="p">);</span> <span class="c1">// unix epoc ( nb of second since 1/1/1970</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> temperature =</span><span class="dl">"</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">main</span><span class="p">.</span><span class="nx">temp</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> pressure =</span><span class="dl">"</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">main</span><span class="p">.</span><span class="nx">pressure</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Error = </span><span class="dl">"</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="p">})();</span> </code></pre></div></div> <h2 id="get-city-weather">get city weather</h2> <p>Let’s write the method that reads the weather of a city.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">_</span><span class="dl">"</span><span class="s2">accessing the openweathermap API key</span><span class="dl">"</span> <span class="kd">const</span> <span class="nx">unirest</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">unirest</span><span class="dl">"</span><span class="p">);</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="nx">city</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">unirest</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span> <span class="dl">"</span><span class="s2">https://community-open-weather-map.p.rapidapi.com/weather?id=2172797</span><span class="dl">"</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">&amp;units=metric</span><span class="dl">"</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">&amp;mode=json</span><span class="dl">"</span> <span class="o">+</span> <span class="s2">`&amp;q=</span><span class="p">${</span><span class="nx">city</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span> <span class="p">.</span><span class="nx">header</span><span class="p">(</span><span class="dl">"</span><span class="s2">X-RapidAPI-Host</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">community-open-weather-map.p.rapidapi.com</span><span class="dl">"</span><span class="p">)</span> <span class="p">.</span><span class="nx">header</span><span class="p">(</span><span class="dl">"</span><span class="s2">X-RapidAPI-Key</span><span class="dl">"</span><span class="p">,</span> <span class="nx">key</span><span class="p">)</span> <span class="p">.</span><span class="nx">end</span><span class="p">(</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">resolve</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">);</span> <span class="p">});</span> <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">status</span> <span class="o">!==</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">API error</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">result</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span> <span class="p">}</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">extract useful data</span><span class="dl">"</span> </code></pre></div></div> <h3 id="extract-useful-data">extract useful data</h3> <p>The <code class="language-plaintext highlighter-rouge">extractUsefulData</code> function convert the raw data retrieved from the API,into a simpler Javascript object for our application.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">function</span> <span class="nx">unixEpoqToDate</span><span class="p">(</span><span class="nx">unixDate</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">d</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="nx">d</span><span class="p">.</span><span class="nx">setUTCSeconds</span><span class="p">(</span><span class="nx">unixDate</span><span class="p">);</span> <span class="k">return</span> <span class="nx">d</span><span class="p">;</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">extractUsefulData</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p">{</span> <span class="na">city</span><span class="p">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">city</span><span class="p">,</span> <span class="na">date</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(),</span> <span class="na">observation_time</span><span class="p">:</span> <span class="nx">unixEpoqToDate</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">dt</span><span class="p">),</span> <span class="na">temperature</span><span class="p">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">main</span><span class="p">.</span><span class="nx">temp</span><span class="p">,</span> <span class="na">humidity</span><span class="p">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">main</span><span class="p">.</span><span class="nx">humidity</span><span class="p">,</span> <span class="na">pressure</span><span class="p">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">main</span><span class="p">.</span><span class="nx">pressure</span><span class="p">,</span> <span class="na">weather</span><span class="p">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">weather</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">main</span> <span class="p">};</span> <span class="p">}</span> </code></pre></div></div> <h3 id="reading-data-periodically">reading data periodically</h3> <p>The Weather Station Server will have to query the weather data of a city on a regular basis. However, we have to be careful not to send too many queries to the web-server, as any exceeding requests will get rejected and will lead to a error:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;h1&gt;</span>Developer Over Rate<span class="nt">&lt;/h1&gt;</span> </code></pre></div></div> <p>In NodeJs, the setInterval function can be used to perform a action periodically.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">london_data</span> <span class="o">=</span> <span class="p">{};</span> <span class="nx">setInterval</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">london_data</span> <span class="o">=</span> <span class="nx">extractUsefulData</span><span class="p">(</span><span class="k">await</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="dl">"</span><span class="s2">London</span><span class="dl">"</span><span class="p">));</span> <span class="p">},</span> <span class="mi">60</span><span class="o">*</span><span class="mi">1000</span><span class="p">);</span> </code></pre></div></div> <p>Lets edit <a href="#making-a-round-robin-read" title="save:">worldweather_demo2.js</a> to experiment this.</p> <h2 id="making-a-round-robin-read">making a round robin read</h2> <p>Why not make our server expose the weather variables of more than one city ? Supposing we hold an array containing the city we want to monitor, our periodic call to the REST API will have to query the data for each city in turn. We will store the most up to date weather data inside a map <code class="language-plaintext highlighter-rouge">city_data_map</code> .</p> <p>I chose 10 cities spread in different continents and hemisphere.</p> <p>Just to make it fun, I added Longyearbyen, the <a href="http://en.wikipedia.org/wiki/Northernmost_cities_and_towns">northenmost city in the world</a> and ‘Ushuaia’ one of the <a href="http://en.wikipedia.org/wiki/Southernmost_cities_and_towns">southernmost city</a>.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*global require,setInterval,console */</span> <span class="kd">const</span> <span class="nx">cities</span> <span class="o">=</span> <span class="p">[</span> <span class="dl">'</span><span class="s1">London</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Paris</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">New York</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Moscow</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Ho chi min</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Benjing</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Reykjavik</span><span class="dl">'</span> <span class="p">,</span><span class="dl">'</span><span class="s1">Nouakchott</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">Ushuaia</span><span class="dl">'</span> <span class="p">,</span><span class="dl">'</span><span class="s1">Longyearbyen</span><span class="dl">'</span><span class="p">];</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">get city weather</span><span class="dl">"</span> <span class="kd">const</span> <span class="nx">city_data_map</span> <span class="o">=</span> <span class="p">{</span> <span class="p">};</span> <span class="c1">// a infinite round-robin iterator over the city array</span> <span class="kd">const</span> <span class="nx">next_city</span> <span class="o">=</span> <span class="p">((</span><span class="nx">arr</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">counter</span> <span class="o">=</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="k">return</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">counter</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="nx">counter</span><span class="o">&gt;=</span><span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span> <span class="nx">counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">counter</span><span class="p">];</span> <span class="p">};</span> <span class="p">})(</span><span class="nx">cities</span><span class="p">);</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">update_city_data</span><span class="p">(</span><span class="nx">city</span><span class="p">)</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">getCityWeather</span><span class="p">(</span><span class="nx">city</span><span class="p">);</span> <span class="nx">city_data_map</span><span class="p">[</span><span class="nx">city</span><span class="p">]</span> <span class="o">=</span> <span class="nx">extractUsefulData</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">error city</span><span class="dl">"</span><span class="p">,</span><span class="nx">city</span> <span class="p">,</span> <span class="nx">err</span><span class="p">);</span> <span class="k">return</span> <span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">// make a API call every 10 seconds</span> <span class="kd">const</span> <span class="nx">interval</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span> <span class="nx">setInterval</span><span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">city</span> <span class="o">=</span> <span class="nx">next_city</span><span class="p">();</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">updating city =</span><span class="dl">"</span><span class="p">,</span><span class="nx">city</span><span class="p">);</span> <span class="k">await</span> <span class="nx">update_city_data</span><span class="p">(</span><span class="nx">city</span><span class="p">);</span> <span class="p">},</span> <span class="nx">interval</span><span class="p">);</span> </code></pre></div></div> <h3 id="weather-server-skeleton">Weather Server Skeleton</h3> <p>It is now time to create the skeleton of our weather server.</p> <p><a href="#Weather-Server-Skeleton" title="save:">weather.js</a></p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*global require,console,setInterval */</span> <span class="nb">Error</span><span class="p">.</span><span class="nx">stackTraceLimit</span> <span class="o">=</span> <span class="kc">Infinity</span><span class="p">;</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">making a round robin read</span><span class="dl">"</span> <span class="kd">const</span> <span class="nx">opcua</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">node-opcua</span><span class="dl">"</span><span class="p">);</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">construct the address space</span><span class="dl">"</span> <span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">server instantiation</span><span class="dl">"</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Error = </span><span class="dl">"</span><span class="p">,</span><span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="p">})();</span> </code></pre></div></div> <h4 id="server-instantiation">server instantiation</h4> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">const</span> <span class="nx">server</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">OPCUAServer</span><span class="p">({</span> <span class="na">port</span><span class="p">:</span> <span class="mi">4334</span><span class="p">,</span> <span class="c1">// the port of the listening socket of the servery</span> <span class="na">buildInfo</span><span class="p">:</span> <span class="p">{</span> <span class="na">productName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">WeatherStation</span><span class="dl">"</span><span class="p">,</span> <span class="na">buildNumber</span><span class="p">:</span> <span class="dl">"</span><span class="s2">7658</span><span class="dl">"</span><span class="p">,</span> <span class="na">buildDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="mi">2019</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">14</span><span class="p">),</span> <span class="p">}</span> <span class="p">});</span> <span class="k">await</span> <span class="nx">server</span><span class="p">.</span><span class="nx">initialize</span><span class="p">();</span> <span class="nx">construct_my_address_space</span><span class="p">(</span><span class="nx">server</span><span class="p">);</span> <span class="k">await</span> <span class="nx">server</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Server is now listening ... ( press CTRL+C to stop)</span><span class="dl">"</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">port </span><span class="dl">"</span><span class="p">,</span> <span class="nx">server</span><span class="p">.</span><span class="nx">endpoints</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">port</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">endpointUrl</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">endpoints</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">endpointDescriptions</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">endpointUrl</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2"> the primary server endpoint url is </span><span class="dl">"</span><span class="p">,</span> <span class="nx">endpointUrl</span> <span class="p">);</span> </code></pre></div></div> <h4 id="construct-the-address-space">construct the address space</h4> <p>The server address space will be made of a <code class="language-plaintext highlighter-rouge">Cities</code> folder containing one folder for each city.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">construct_my_address_space</span><span class="p">(</span><span class="nx">server</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// declare some folders</span> <span class="kd">const</span> <span class="nx">addressSpace</span> <span class="o">=</span> <span class="nx">server</span><span class="p">.</span><span class="nx">engine</span><span class="p">.</span><span class="nx">addressSpace</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">namespace</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">getOwnNamespace</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">objectsFolder</span> <span class="o">=</span> <span class="nx">addressSpace</span><span class="p">.</span><span class="nx">rootFolder</span><span class="p">.</span><span class="nx">objects</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">citiesNode</span> <span class="o">=</span> <span class="nx">namespace</span><span class="p">.</span><span class="nx">addFolder</span><span class="p">(</span><span class="nx">objectsFolder</span><span class="p">,{</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Cities</span><span class="dl">"</span><span class="p">});</span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">city_name</span> <span class="k">of</span> <span class="nx">cities</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// declare the city node</span> <span class="kd">const</span> <span class="nx">cityNode</span> <span class="o">=</span> <span class="nx">namespace</span><span class="p">.</span><span class="nx">addFolder</span><span class="p">(</span><span class="nx">citiesNode</span><span class="p">,{</span> <span class="na">browseName</span><span class="p">:</span> <span class="nx">city_name</span> <span class="p">});</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">construct city weather variables</span><span class="dl">"</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">_</span><span class="dl">"</span><span class="s2">extracting a DataValue</span><span class="dl">"</span> </code></pre></div></div> <h4 id="extracting-a-datavalue">extracting a DataValue</h4> <p>Let’s write a helper function (<code class="language-plaintext highlighter-rouge">extract_value</code>) to extract a city weather variable as DataValue. Since the city weather data are read asynchronously at a very low rate, it is possible that the data doesn’t exist yet when the client will send its request. We have to be careful to handle this case appropriately. In the absence of city data, I have chose to send a StatusCodes.UncertainInitalValue status code.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">dataType</span><span class="p">,</span><span class="nx">city_name</span><span class="p">,</span><span class="nx">property</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">city</span> <span class="o">=</span> <span class="nx">city_data_map</span><span class="p">[</span><span class="nx">city_name</span><span class="p">];</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">city</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">StatusCodes</span><span class="p">.</span><span class="nx">BadDataUnavailable</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">city</span><span class="p">[</span><span class="nx">property</span><span class="p">];</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">opcua</span><span class="p">.</span><span class="nx">Variant</span><span class="p">({</span><span class="nx">dataType</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="nx">value</span> <span class="p">});</span> <span class="p">}</span> </code></pre></div></div> <h4 id="construct-city-weather-variables">construct city weather variables</h4> <p>Each city node exposes 3 read-only variables that can be instantiated this way:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">namespace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">cityNode</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Temperature</span><span class="dl">"</span><span class="p">,</span> <span class="na">nodeId</span><span class="p">:</span> <span class="s2">`s=</span><span class="p">${</span><span class="nx">city_name</span><span class="p">}</span><span class="s2">-Temperature`</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span> <span class="nx">city_name</span><span class="p">,</span><span class="dl">"</span><span class="s2">temperature</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="nx">namespace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">cityNode</span><span class="p">,</span> <span class="na">nodeId</span><span class="p">:</span> <span class="s2">`s=</span><span class="p">${</span><span class="nx">city_name</span><span class="p">}</span><span class="s2">-Humidity`</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Humidity</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span><span class="nx">city_name</span><span class="p">,</span><span class="dl">"</span><span class="s2">humidity</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="nx">namespace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">cityNode</span><span class="p">,</span> <span class="na">nodeId</span><span class="p">:</span> <span class="s2">`s=</span><span class="p">${</span><span class="nx">city_name</span><span class="p">}</span><span class="s2">-Pressure`</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Pressure</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Double</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nx">Double</span><span class="p">,</span><span class="nx">city_name</span><span class="p">,</span><span class="dl">"</span><span class="s2">pressure</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> <span class="nx">namespace</span><span class="p">.</span><span class="nx">addVariable</span><span class="p">({</span> <span class="na">componentOf</span><span class="p">:</span> <span class="nx">cityNode</span><span class="p">,</span> <span class="na">nodeId</span><span class="p">:</span> <span class="s2">`s=</span><span class="p">${</span><span class="nx">city_name</span><span class="p">}</span><span class="s2">-Weather`</span><span class="p">,</span> <span class="na">browseName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Weather</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">String</span><span class="dl">"</span><span class="p">,</span> <span class="na">value</span><span class="p">:</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">extract_value</span><span class="p">(</span><span class="nx">opcua</span><span class="p">.</span><span class="nx">DataType</span><span class="p">.</span><span class="nb">String</span><span class="p">,</span><span class="nx">city_name</span><span class="p">,</span><span class="dl">"</span><span class="s2">weather</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">});</span> </code></pre></div></div> <h3 id="testing-the-server">testing the server</h3> <p>It is now time to start the server for testing.</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>node weather.js </code></pre></div></div> <p>Putting everything together, the <code class="language-plaintext highlighter-rouge">weather.js</code> script looks like <a href="https://raw.githubusercontent.com/node-opcua/node-opcua/master/documentation/weather.js">this</a></p> Sun, 05 Jul 2015 11:00:00 +0000 https://node-opcua.github.io/tutorial/2015/07/05/weather-station.html https://node-opcua.github.io/tutorial/2015/07/05/weather-station.html tutorial