Node-OPCUA Node-OPCUA, the OPCUA SDK for node.js. https://node-opcua.github.io/ Wed, 24 Feb 2021 21:21:04 +0000 Wed, 24 Feb 2021 21:21:04 +0000 Jekyll v3.9.0 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 OPCUA 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-opcua-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 weather station in node-opcua <h1 id="creating-a-opcua-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. 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 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 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 need 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 you 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, 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>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