<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:discourse="http://discourse.org/rss/modules/discourse/"><channel><title>Diogo Domingues on DevBlog</title><link>https://devblog.criticalmanufacturing.com/author/diogo-domingues/</link><description>Recent content in Diogo Domingues on DevBlog</description><image><url>https://devblog.criticalmanufacturing.com/uploads/og.webp</url><link>https://devblog.criticalmanufacturing.com/uploads/og.webp</link></image><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>A template by [Heksagon](https://www.heksagon.net). Implemented for [Critical Manufacturing](https://www.criticalmanufacturing.com/).</copyright><lastBuildDate>Mon, 29 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://devblog.criticalmanufacturing.com/author/diogo-domingues/index.xml" rel="self" type="application/rss+xml"/><item><title>Automatic IoT Data Synchronization After Communication Loss</title><link>https://devblog.criticalmanufacturing.com/blog/20250929_iot_data_synchronization/</link><pubDate>Mon, 29 Sep 2025 00:00:00 +0000</pubDate><dc:creator>Diogo Domingues</dc:creator><guid>https://devblog.criticalmanufacturing.com/blog/20250929_iot_data_synchronization/</guid><description>Keeping IoT Data Consistent</description><content:encoded><![CDATA[<h2 id="-the-need-for-automatic-iot-data-synchronization-after-communication-loss">🔄 The Need for Automatic IoT Data Synchronization After Communication Loss</h2>
<p>In modern industrial systems, IoT connectivity is vital, but even the most robust networks can experience downtime. Whether caused by network instability, device downtime, or maintenance operations, these interruptions can disrupt the data flow between shop-floor systems and higher-level applications.</p>
<p>Now imagine this scenario: your MES continues processing operations while IoT communication is down, and the IoT tags are managed through an OPCUA Server. Once the connection is restored, the OPCUA Server still holds outdated tag values, no longer aligned with the actual operations executed in the MES. This mismatch creates a gap that can lead to data inconsistencies, missed updates, and even operational errors.</p>
<h2 id="-the-challenge-keeping-iot-data-consistent">🎯 The Challenge: Keeping IoT Data Consistent</h2>
<p>During the implementation of a certain project using MES version 11.1, a customer in the testing phase highlighted exactly this scenario. They wanted the MES to remain fully operational even if IoT communication went down, but also needed the OPCUA Server to be automatically updated with the latest tags as soon as communication came back online.</p>
<p>This requirement translated into a need for IoT data synchronization upon communication failure and subsequent reconnection, essentially, an automated way to reconcile and update tags once IoT was back online.</p>
<p>In this particular project, I was using the <strong>OPCUA Template – DynamicTags</strong>, an automation controller previously described in another blog post (<a href="20240521_iot_dynamictags.md">OPCUA Template - DynamicTags</a>). As outlined there, this template aggregates customer requirements for OPCUA implementations and is continuously evolving to address common scenarios such as data collections, machine state changes, service and DEE calls, and recipe management.</p>
<p>As the template did not offer this type of synchronization by default, I designed and implemented the logic directly within it. In the following sections, I’ll walk you through my approach to solving this problem by extending the <strong>OPCUA Template – DynamicTags</strong> with an automatic synchronization mechanism with the update of <strong>Write Tags</strong> and <strong>Setup</strong> workflows. This ensures that OPCUA tags always catch up with the latest MES state after a communication failure, keeping workflows consistent.</p>
<h2 id="-write-tags-workflow">📝 Write Tags Workflow</h2>
<p>The <strong>Write Tags</strong> workflow is responsible for writing values to the tags on the OPCUA Server. It can be triggered by any other workflow, and whenever it is called, its purpose is to update one or more tags. However, if the communication is down at that moment, the request would normally fail.</p>
<p>To address this, I updated the code task responsible for writing tags to the OPCUA Server so that it first persists the values before attempting the write. This way, persistence always holds the most up-to-date information, regardless of the current communication state. As a result, when IoT communication is restored, the latest data can be retrieved directly from persistence and immediately synchronized with the OPCUA Server.</p>
<p>
<img src="/blogPosts/IoT/20250929_iot_data_synchronization/WriteTagsWorkflow.png" alt="WriteTagsWorkflow" />

</p>
<p>➡️ Here’s a snippet from the updated code task in the <strong>Write Tags</strong> workflow:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// Store in persistency the tags information
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">framework</span>.<span style="color:#a6e22e">dataStore</span>.<span style="color:#a6e22e">store</span>(<span style="color:#e6db74">&#34;TagsInformation&#34;</span>, <span style="color:#a6e22e">propertiesToSet</span>, <span style="color:#e6db74">&#34;Persistent&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Send request to driver to setPropertiesValues
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">let</span> <span style="color:#a6e22e">jsonReceivedSet</span>: <span style="color:#66d9ef">any</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">framework</span>.<span style="color:#a6e22e">driver</span>.<span style="color:#a6e22e">sendRaw</span>(<span style="color:#e6db74">&#34;connect.iot.driver.opcua.setPropertiesValues&#34;</span>, <span style="color:#a6e22e">propertiesToSet</span>);
</span></span></code></pre></div><h2 id="-setup-workflow">⚙️ Setup Workflow</h2>
<p>The <strong>Setup</strong> workflow is responsible for managing the controller’s communication. In the original version of the controller, the <code>onCommunicationChange</code> output from the <code>On Equipment Setup</code> task was already linked to a code task. That task was designed to execute specific logic whenever the state changed to <code>Disconnected</code>.</p>
<p>Therefore, I reused the same output and code task but extended its logic: now, when the state changes to <code>Communicating</code>, the workflow retrieves the updated tag values from the persistence, which has been continuously maintained by the <strong>Write Tags</strong> workflow and immediately sends them to the OPCUA Server. This ensures that the server is always synchronized with the MES after a communication failure and subsequent reconnection.</p>
<p>
<img src="/blogPosts/IoT/20250929_iot_data_synchronization/SetupWorkflow.png" alt="SetupWorkflow" />

</p>
<p>➡️ Here’s a snippet from the updated code task in the <strong>Setup</strong> workflow:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">state</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;Disconnected&#34;</span>) {
</span></span><span style="display:flex;"><span>    ...
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">state</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">&#34;Communicating&#34;</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">propertiesToSet</span>: <span style="color:#66d9ef">OutputProperty</span>[] <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">framework</span>.<span style="color:#a6e22e">dataStore</span>.<span style="color:#a6e22e">retrieve</span>(<span style="color:#e6db74">&#34;TagsInformation&#34;</span>, []);
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">propertiesToSet</span><span style="color:#f92672">?</span>.<span style="color:#a6e22e">length</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">deviceValues</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">propertiesToSet</span>.<span style="color:#a6e22e">map</span>(<span style="color:#a6e22e">p</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">property</span>.<span style="color:#a6e22e">deviceId</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> = &#39;</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">value</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#39;`</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">framework</span>.<span style="color:#a6e22e">logger</span>.<span style="color:#a6e22e">info</span>(<span style="color:#e6db74">`Will synchronize tag values</span><span style="color:#960050;background-color:#1e0010">\</span><span style="color:#e6db74">n</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">deviceValues</span>.<span style="color:#a6e22e">join</span>(<span style="color:#e6db74">&#39;\n&#39;</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Send request to driver to setPropertiesValues
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>        <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">jsonReceivedSet</span>: <span style="color:#66d9ef">any</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">framework</span>.<span style="color:#a6e22e">driver</span>.<span style="color:#a6e22e">sendRaw</span>(<span style="color:#e6db74">&#34;connect.iot.driver.opcua.setPropertiesValues&#34;</span>, <span style="color:#a6e22e">propertiesToSet</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">framework</span>.<span style="color:#a6e22e">logger</span>.<span style="color:#a6e22e">debug</span>(<span style="color:#e6db74">`setPropertiesValues received: </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>(<span style="color:#a6e22e">jsonReceivedSet</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="-new-runtime-behavior">🚀 New Runtime Behavior</h2>
<p>After the aforementioned changes, I recreated the scenario with both the Automation Manager and the OPCUA server running locally. Previously, a disconnection would cause the manager to keep attempting to reconnect, without taking further action. Currently, it also automatically retrieves the stored information and immediately updates the server tags, as shown in the figure below:</p>
<p>
<img src="/blogPosts/IoT/20250929_iot_data_synchronization/SynchronizeTagsLogs.png" alt="SynchronizeTagsLogs" />

</p>
<h2 id="-applying-it-in-your-project">✅ Applying It in Your Project</h2>
<p>This logic is already available in <strong>COMMON</strong> repository, within the <strong>Dynamic Tags Template</strong>. So, if you are planning to use the template and need this functionality, all you have to do is include the dependency in your project, as explained in the blog post <a href="20240521_iot_dynamictags.md">OPCUA Template - DynamicTags</a>.</p>
<p>If you do not intend to use the <strong>Dynamic Tags Template</strong>, the explanation above still provides a clear approach that can be applied to similar scenarios in other implementations.</p>
<h2 id="-author">👨‍💻 Author</h2>
<h3 id="hi-my-name-is-diogo-domingues-">Hi! My name is Diogo Domingues. ✌️</h3>
<p>I&rsquo;m a Software Engineer at Critical Manufacturing, working in the Solution Delivery area.</p>
<p>You can find me on <a href="https://www.linkedin.com/in/diogo-domingues-875701228/" target="_blank" rel="noopener">LinkedIn</a>.</p>
<p>
<figure class="text-center">
  <img
    src="/blogPosts/contributors/blogger_diogo_domingues.jpg"
    alt="Diogo Domingues"
    title="Software Engineer"
  />
  <figcaption style="text-align: center">Software Engineer</figcaption>
</figure>

</p>
]]></content:encoded></item></channel></rss>