Skip to content

Commit

Permalink
Add documentation on OPCUA to Semantic Web transformation
Browse files Browse the repository at this point in the history
An overview and tutorial has been added which shows how to
transform companion specifications and instance nodeset2.xml
into semantic WEB, i.e. OWL, SHACL and NGSI-LD

Signed-off-by: marcel <[email protected]>
  • Loading branch information
wagmarcel authored and abhijith-hr committed Nov 28, 2024
1 parent 8f20d9a commit 08d05f6
Show file tree
Hide file tree
Showing 12 changed files with 504 additions and 2 deletions.
24 changes: 24 additions & 0 deletions semantic-model/opcua/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Table of Contents

1. [Overview & Setup](./overview.md)
2. [Example & Tutorial](./simple-example.md)
3. [Building Companion Specifications](./building-companion-specifications.md)
3. [Mapping concept](./mapping-concept.md)
5. [Tools](./tools.md)


# References

[1] W3C. (2017). SHACL (Shapes Constraint Language). W3C Recommendation. Available at: https://www.w3.org/TR/shacl/

[2] W3C. (2012). OWL 2 Web Ontology Language: Document Overview. W3C Recommendation. Available at: https://www.w3.org/TR/owl2-overview/

[3] W3C. (2020). JSON-LD 1.1: A JSON-based Serialization for Linked Data. W3C Recommendation. Available at: https://www.w3.org/TR/json-ld11/

[4] ETSI. (2019). NGSI-LD API: Linked Data-based API for context information management. ETSI GS CIM 009 V1.1.1. Available at: https://www.etsi.org/deliver/etsi_gs/CIM/001_099/009/01.08.01_60/gs_cim009v010801p.pdf

[5] W3C. (2014). Turtle - Terse RDF Triple Language. W3C Recommendation. Available at: https://www.w3.org/TR/turtle/

[6] Berners-Lee, T., Hendler, J., & Lassila, O. (2001). The Semantic Web. Scientific American, 284(5), 34-43.

[7] Protege, https://protege.stanford.edu/
86 changes: 86 additions & 0 deletions semantic-model/opcua/docs/building-companion-specifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Convert Companion Specifications

Companion specifications and their dependencies must be converted to ensure that all dependencies are fulfilled. The specifications can be built for *local* testing with *local* dependencies or for *global* use with IRIs. The main difference is that for *local* testing, the dependencies are referencing the local filesystem and can only be used on the current *local* system whereas the *globallly* built specifications can be imported or used from everywhere with Internet access.

In OWL, dependencies are managed by `owl:imports` triples. A *global* import contains an IRI like so:

owl:imports <https://industryfusion.github.io/contexts/staging/ontology/v0.1/base.ttl> ;

In contrast, a *local* import is referencing the local directories in a File URI, for instance:

owl:imports </home/user/src/IndustryFusion/DigitalTwin/semantic-model/opcua/core.ttl>

For convenience, a script is provided to create the "usual suspect" companion specifications (note nodeset version: `NODESET_VERSION=UA-1.05.03-2023-12-15`):

bash ./translate_default_specs.bash

creates the *local* version of the specifications.

The *global* version of the specifications can be created as follows:

bash ./translate_default_specs.bash remote


## Convert Specific Companion Specifications

Currently there is no autdetection of dependencies. Therefore, a conversion must add the depenencies manually. Every specification is dependend on the base ontology:

<https://industryfusion.github.io/contexts/staging/ontology/v0.1/base.ttl>

For instance, converting the core OPCUA specification looks as follows:


NODESET_VERSION=UA-1.05.03-2023-12-15
CORE_NODESET=https://raw.githubusercontent.com/OPCFoundation/UA-Nodeset/${NODESET_VERSION}/Schema/Opc.Ua.NodeSet2.xml
BASE_ONTOLOGY=https://industryfusion.github.io/contexts/staging/ontology/v0.1/base.ttl
python3 nodeset2owl.py ${CORE_NODESET} -i ${BASE_ONTOLOGY} -p opcua -o core.ttl

The `DI` specification is translated as follows:

NODESET_VERSION=UA-1.05.03-2023-12-15
CORE_NODESET=https://raw.githubusercontent.com/OPCFoundation/UA-Nodeset/${NODESET_VERSION}/Schema/Opc.Ua.NodeSet2.xml
BASE_ONTOLOGY=https://industryfusion.github.io/contexts/staging/ontology/v0.1/base.ttl
CORE_ONTOLOGY=core.ttl
python3 nodeset2owl.py ${DI_NODESET} -i ${BASE_ONTOLOGY} ${CORE_ONTOLOGY} -p devices -o devices.ttl

And the `Machinery` specification is converted like so:

NODESET_VERSION=UA-1.05.03-2023-12-15
CORE_NODESET=https://raw.githubusercontent.com/OPCFoundation/UA-Nodeset/${NODESET_VERSION}/Schema/Opc.Ua.NodeSet2.xml
BASE_ONTOLOGY=https://industryfusion.github.io/contexts/staging/ontology/v0.1/base.ttl
MACHINERY_NODESET=https://raw.githubusercontent.com/OPCFoundation/UA-Nodeset/${NODESET_VERSION}/Machinery/Opc.Ua.Machinery.NodeSet2.xml
CORE_ONTOLOGY=core.ttl
DEVICES_ONTOLOGY=devices.ttl
python3 nodeset2owl.py ${MACHINERY_NODESET} -i ${BASE_ONTOLOGY} ${CORE_ONTOLOGY} ${DEVICES_ONTOLOGY} -p machinery -o machinery.ttl

In general, to determine the dependencies, the `<Models>` section of the target Nodeset must be analyzed. In the Pumps specification it looks e.g. like this

```
<Models>
<Model ModelUri="http://opcfoundation.org/UA/Pumps/" Version="1.0.0" PublicationDate="2021-04-19T00:00:00Z">
<RequiredModel ModelUri="http://opcfoundation.org/UA/" Version="1.04.7" PublicationDate="2020-07-15T00:00:00Z" />
<RequiredModel ModelUri="http://opcfoundation.org/UA/DI/" Version="1.02.2" PublicationDate="2020-06-02T00:00:00Z" />
<RequiredModel ModelUri="http://opcfoundation.org/UA/Machinery/" Version="1.0.0" PublicationDate="2020-09-25T00:00:00Z" />
</Model>
</Models>
```
and this suggest that the nodeset is (besides the base specification) dependends on `core.ttl`, `devices.ttl` and `machinery.ttl`. The conversation would then look like:

```
NODESET_VERSION=UA-1.05.03-2023-12-15
PUMPS_NODESET=https://raw.githubusercontent.com/OPCFoundation/UA-Nodeset/${NODESET_VERSION}/Pumps/Opc.Ua.Pumps.NodeSet2.xml
BASE_ONTOLOGY=https://industryfusion.github.io/contexts/staging/ontology/v0.1/base.ttl
python3 nodeset2owl.py ${PUMPS_NODESET} -i ${BASE_ONTOLOGY} core.ttl devices.ttl machinery.ttl -p pumps -o pumps.ttl
```

## Extract SHACL and JSON-LD from instances



## Global Specifications

IndustrsFusion Foundation is offering a set of `usual` suspects here:

https://industryfusion.github.io/contexts/staging/opcua/v0.1/
157 changes: 157 additions & 0 deletions semantic-model/opcua/docs/files/Example.NodeSet2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
-->

<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" LastModified="2024-07-15T00:00:00Z" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
<Models>
<Model ModelUri="http://my.test/" XmlSchemaUri="http://opcfoundation.org/UA/2008/02/Types.xsd" Version="1.05.03" PublicationDate="2024-07-29T00:00:00Z" ModelVersion="1.5.3" />
</Models>
<NamespaceUris>
<Uri>http://my.test/</Uri>
</NamespaceUris>
<Aliases>
<Alias Alias="Boolean">i=1</Alias>
<Alias Alias="SByte">i=2</Alias>
<Alias Alias="Byte">i=3</Alias>
<Alias Alias="Int16">i=4</Alias>
<Alias Alias="UInt16">i=5</Alias>
<Alias Alias="Int32">i=6</Alias>
<Alias Alias="UInt32">i=7</Alias>
<Alias Alias="Int64">i=8</Alias>
<Alias Alias="UInt64">i=9</Alias>
<Alias Alias="Float">i=10</Alias>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="DateTime">i=13</Alias>
<Alias Alias="String">i=12</Alias>
<Alias Alias="ByteString">i=15</Alias>
<Alias Alias="Guid">i=14</Alias>
<Alias Alias="XmlElement">i=16</Alias>
<Alias Alias="NodeId">i=17</Alias>
<Alias Alias="ExpandedNodeId">i=18</Alias>
<Alias Alias="QualifiedName">i=20</Alias>
<Alias Alias="LocalizedText">i=21</Alias>
<Alias Alias="StatusCode">i=19</Alias>
<Alias Alias="Structure">i=22</Alias>
<Alias Alias="Number">i=26</Alias>
<Alias Alias="Integer">i=27</Alias>
<Alias Alias="UInteger">i=28</Alias>
<Alias Alias="HasComponent">i=47</Alias>
<Alias Alias="HasProperty">i=46</Alias>
<Alias Alias="Organizes">i=35</Alias>
<Alias Alias="HasEventSource">i=36</Alias>
<Alias Alias="HasNotifier">i=48</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasEncoding">i=38</Alias>
<Alias Alias="HasDescription">i=39</Alias>
<Alias Alias="HasCause">i=53</Alias>
<Alias Alias="ToState">i=52</Alias>
<Alias Alias="FromState">i=51</Alias>
<Alias Alias="HasEffect">i=54</Alias>
<Alias Alias="HasTrueSubState">i=9004</Alias>
<Alias Alias="HasFalseSubState">i=9005</Alias>
<Alias Alias="HasDictionaryEntry">i=17597</Alias>
<Alias Alias="HasCondition">i=9006</Alias>
<Alias Alias="HasGuard">i=15112</Alias>
<Alias Alias="HasAddIn">i=17604</Alias>
<Alias Alias="HasInterface">i=17603</Alias>
</Aliases>

<UAReferenceType NodeId="ns=1;i=100" BrowseName="X" IsAbstract="true" Symmetric="false">
<DisplayName>X Reference</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
</UAReferenceType>
<UAReferenceType NodeId="ns=1;i=101" BrowseName="Y" IsAbstract="true" Symmetric="false">
<DisplayName>Y Reference</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
</UAReferenceType>
<UAObjectType NodeId="ns=1;i=1001" BrowseName="1:AlphaType">
<DisplayName>AlphaType</DisplayName>
<Description>A custom object type Alpha</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
<Reference ReferenceType="ns=1;i=101" IsForward="true">ns=1;i=2001</Reference>
<Reference ReferenceType="ns=1;i=100" IsForward="true">ns=1;i=1003</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=1003</Reference>
</References>
</UAObjectType>
<UAObjectType NodeId="ns=1;i=1002" BrowseName="1:BType">
<DisplayName>B Type</DisplayName>
<Description>A custom object type B</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAObject NodeId="ns=1;i=1003" BrowseName="1:B">
<DisplayName>Object B</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">ns=1;i=1002</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=1004</Reference>
</References>
</UAObject>
<UAVariable NodeId="ns=1;i=1004" BrowseName="MyVariable" DataType="Boolean">
<DisplayName>Variable of B-object</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">i=63</Reference>
</References>
</UAVariable>
<UAObjectType NodeId="ns=1;i=1005" BrowseName="1:BSubType">
<DisplayName>B Sub Type</DisplayName>
<Description>A custom subobject of type B</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">ns=1;i=1002</Reference>
</References>
</UAObjectType>


<UAVariable NodeId="ns=1;i=2001" BrowseName="1:C" DataType="i=11">
<DisplayName>Variable of AlphaType</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1001</Reference>
</References>
</UAVariable>
<UAVariable NodeId="ns=1;i=2002" BrowseName="1:E" DataType="i=11">
<DisplayName>Variable of Variable C</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=2001</Reference>
</References>
</UAVariable>
<UAObject NodeId="ns=1;i=2010" BrowseName="1:AlphaInstance">
<DisplayName>Alpha Instance</DisplayName>
<References>
<Reference ReferenceType="HasComponent" IsForward="true">ns=1;i=2011</Reference>
<Reference ReferenceType="HasComponent" IsForward="true">ns=1;i=2012</Reference>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">ns=1;i=1001</Reference>
<Reference ReferenceType="ns=1;i=101" IsForward="true">ns=1;i=2012</Reference>
</References>
</UAObject>
<UAVariable NodeId="ns=1;i=2011" BrowseName="1:C" DataType="i=11">
<DisplayName>C Variable of AlphaType</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=2010</Reference>
<Reference ReferenceType="ns=1;i=100" IsForward="true">ns=1;i=2011</Reference>
</References>
</UAVariable>
<UAObject NodeId="ns=1;i=2012" BrowseName="1:B">
<DisplayName>Object B subtype</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">ns=1;i=1005</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=2013</Reference>
</References>
</UAObject>
<UAVariable NodeId="ns=1;i=2013" BrowseName="MyVariable" DataType="Boolean">
<DisplayName>Variable of B-object</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition" IsForward="true">i=63</Reference>
</References>
</UAVariable>
</UANodeSet>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Semantic Data of OPCUA
# Mapping Concept

## Conversion to NGSI-LD(JSON-LD)

In the following the mapping of OPCUA information model to NGSI-LD is described.
This is the information model we use as refernce
Expand Down Expand Up @@ -80,7 +82,7 @@ Properties of Objects are only differentiable from Datavariables by metadata pro
"hasPropertyZ": false
}
```
## Semantic conversion of OPCUA Object-arrays
## Conversion of OPCUA Object-arrays

Objects which are part of an array are typcially defined with a template <> definition. E.g. object_<no> means that there could be object_1, object_2, ... browsepath.
The problem of this is that the name convention is not exactly specified, so object_#1, object#2, ... or object_01, object_02, ... is also possible. Moreover, this makes it difficult to write logic which addresses all element in an array because one needs to guess the names with a pattern. Therefore, we treat this case different. A NGSI-LD instance of such an object would look as follows:
Expand Down
52 changes: 52 additions & 0 deletions semantic-model/opcua/docs/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Overview

This document describes how to map OPCUA data into Semantic Web [6] data, more specifically we describe how to transform OPCUA data into the following 3 standards:
1. The **constraints and rules** are expressed in the SHApes Constraint Language (SHACL) [1] (`shacl.ttl`)
2. The **ontology** data about types, enumerations and other explicit knowledge e is expressed in the Web Ontology Language (OWL)[2] (called `entities.ttl`, or sometimes `knowledge.ttl` or `ontolgoy.ttl`)
3. Representation of the OPCUA **instance** as JSON-LD [3] or more specifically the NGSI-LD[4] standard (called `instances.jsonld`)

The files are all represented in Resource Description Format[6] serialized in the Turtle[5] or JSON-LD.


# Setup for Linux/Windows

## For Linux and Windows
Target System Linux, tested on `Ubuntu 22.04`.

In additiona the following must be installed:

- Python3 >= 3.10
- VSCode
- Make, bash, git (for Linux)
- GitBash (for Windows)

Get the code from the IndustryFusion Foundation repo:

```
git clone https://github.com/IndustryFusion/DigitalTwin.git
```

Find the right directory:

```
cd DigitalTwin/semantic-model/opcua/
```

and install the dependencies:

```
make setup
```

## For Windows only

In this document, the Python 3 executable is named `python3` which can create a mismatch with the Windows installation. Therefore, the gitBash needs to define an alias for `python3`.

code ~/.bashrc

And then add to the end to the file (or to the beginning if it does not exist)

alias python3="python"



Loading

0 comments on commit 08d05f6

Please sign in to comment.