Skip to content

Commit

Permalink
Merge pull request #8 from equinor/story/boundary-application
Browse files Browse the repository at this point in the history
Boundary dotnet app
  • Loading branch information
daghovland authored Aug 21, 2024
2 parents fbdc48b + 63b5b6d commit 0a1905a
Show file tree
Hide file tree
Showing 17 changed files with 421 additions and 42 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/pull_request_tester.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: "Run tests (required)"
on:
workflow_dispatch:
pull_request:
push:
branches:
- main

jobs:
dotnet-tests:
runs-on: ubuntu-latest
name: ".NET Tests & Reports"
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Restore dependencies
run: dotnet restore ./client/Boundaries/Boundaries.sln

- name: Build
run: dotnet build ./client/Boundaries/Boundaries.sln --no-restore

- name: Test
run: |
dotnet test ./client/Boundaries/Boundaries.sln --no-build --verbosity normal
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ dexpi.properties
pandid.xml
pandid.trig
imf-ontology.owl.ttl
rml/segments.trig
client/.idea
22 changes: 22 additions & 0 deletions client/Boundaries/Boundaries.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Boundaries", "Boundaries\Boundaries.csproj", "{B7723633-9BCD-44C6-BC90-1E3F5C6C8101}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBoundaries", "TestBoundaries\TestBoundaries.csproj", "{6389698E-C0E2-4262-AE30-FD2A01654FA5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B7723633-9BCD-44C6-BC90-1E3F5C6C8101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7723633-9BCD-44C6-BC90-1E3F5C6C8101}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7723633-9BCD-44C6-BC90-1E3F5C6C8101}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7723633-9BCD-44C6-BC90-1E3F5C6C8101}.Release|Any CPU.Build.0 = Release|Any CPU
{6389698E-C0E2-4262-AE30-FD2A01654FA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6389698E-C0E2-4262-AE30-FD2A01654FA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6389698E-C0E2-4262-AE30-FD2A01654FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6389698E-C0E2-4262-AE30-FD2A01654FA5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
14 changes: 14 additions & 0 deletions client/Boundaries/Boundaries/Boundaries.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="IriTools" Version="2.2.0" />
</ItemGroup>

</Project>
32 changes: 32 additions & 0 deletions client/Boundaries/Boundaries/DatalogCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using IriTools;

namespace Boundaries;

public class DatalogCreator
{
public IriReference BoundaryGraph = new IriReference($"https://data.equinor.com/boundaries/{Guid.NewGuid()}");

public string CreateCommissioningSparqlQuery()
{
return $"select ?s(GROUP_CONCAT(?label; SEPARATOR=',') AS ?labels) where {{?s a <{BoundaryGraph}>; rdfs:label ?label. OPTIONAL{{?s <http://sandbox.dexpi.org/rdl/TagNameAssignmentClass> ?tag.}} }} GROUP BY (?s)";
}

public string CreateBoundaryDatalogRule(string internalComponentLabel, IriReference[] borderComponentIris)
{
var filters = borderComponentIris
.Select(iri => $"NOT FILTER(?node1 = <{iri}>)")
.Aggregate("", (acc, filter) => acc + ",\n " + filter);
return $$"""
<{{BoundaryGraph}}> [?node] :-
rdfs:label [?internal, "{{internalComponentLabel}}"],
imf:connectedTo [?internal, ?node],
dexpi:PipingOrEquipment [?node].
<{{BoundaryGraph}}> [?node] :-
<{{BoundaryGraph}}> [?node1],
imf:connectedTo [?node1, ?node]{{filters}},
dexpi:PipingOrEquipment [?node].
""";
}
}
45 changes: 45 additions & 0 deletions client/Boundaries/Boundaries/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using IriTools;

namespace Boundaries;

internal class Program
{
private static async Task Main(string[] args)
{
if (args.Length < 3)
{
Console.WriteLine(
"Usage \"dotnet run filename-for-dexpi-in-rdf \"label-of-internal-componenet\" iri-of-border-component ... iri-of-border-component ");
Console.WriteLine(
"For example \"dotnet run ../../../rml/pandid.trig \"T4750\" https://assetid.equinor.com/plantx#Nozzle-12 https://assetid.equinor.com/plantx#Nozzle-8 https://assetid.equinor.com/plantx#PlateHeatExchanger-1 https://assetid.equinor.com/plantx#ReciprocatingPump-1");
return;
}

var dexpiFilePath = args[0];
var internalComponentLabel = args[1];
if (!File.Exists(dexpiFilePath))
{
Console.WriteLine(
$"Could not find one of the input file {dexpiFilePath}");
return;
}

var borderComponentIris = args.Skip(2).Select(iri => new IriReference(iri)).ToArray();
var datalogCreator = new DatalogCreator();
var datalog = datalogCreator.CreateBoundaryDatalogRule(internalComponentLabel, borderComponentIris);
var conn = RdfoxApi.GetDefaultConnectionSettings();
await RdfoxApi.LoadDatalog(conn, datalog);

var data = File.ReadAllText(dexpiFilePath);
await RdfoxApi.LoadData(conn, data);

var queryString = datalogCreator.CreateCommissioningSparqlQuery();
var result = await RdfoxApi.QuerySparql(conn, queryString);
Console.WriteLine("Commissioning package:");
Console.WriteLine(result);

await RdfoxApi.DeleteData(conn, data);
await RdfoxApi.DeleteDatalog(conn, datalog);

}
}
148 changes: 148 additions & 0 deletions client/Boundaries/Boundaries/RdfoxApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System.Text;
using IriTools;

namespace Boundaries;


public class RdfoxApi
{
public struct ConnectionSettings
{
public string Host { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Datastore { get; set; }

}

public static ConnectionSettings GetDefaultConnectionSettings()
{
return new ConnectionSettings
{
Host = "localhost",
Port = 12110,
Username = "admin",
Password = "admin",
Datastore = "boundaries"
};
}


/// <summary>
/// curl -i -X POST localhost:12110/datastores/boundaries/content?operation=delete-content -H "Content-Type: application/x.datalog" -T boundaries.dlog
/// </summary>
/// <param name="conn"></param>
/// <param name="datalog"></param>
public static async Task DeleteDatalog(ConnectionSettings conn, string datalog)
{
using (var client = new HttpClient())
{
var uri = new Uri($"http://{conn.Host}:{conn.Port}/datastores/{conn.Datastore}/content?operation=delete-content");
var content = new StringContent(datalog, Encoding.UTF8, "application/x.datalog");

var request = new HttpRequestMessage(HttpMethod.Patch, uri)
{
Content = content
};

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
}
}

/// <summary>
/// curl -i -X POST localhost:12110/datastores/boundaries/content -H "Content-Type: application/x.datalog" -T boundaries.dlog
/// </summary>
/// <param name="conn"></param>
/// <param name="datalog"></param>
public static async Task LoadDatalog(ConnectionSettings conn, string datalog)
{
using (var client = new HttpClient())
{
var uri = new Uri($"http://{conn.Host}:{conn.Port}/datastores/{conn.Datastore}/content");
var content = new StringContent(datalog, Encoding.UTF8, "application/x.datalog");

var request = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = content
};


var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
}
}

/// <summary>
/// curl -i -X PATCH localhost:12110/datastores/boundaries/content?operation=delete-content -H "Content-Type: application/trig" -T boundaries.dlog
/// </summary>
/// <param name="conn"></param>
/// <param name="datalog"></param>
public static async Task DeleteData(ConnectionSettings conn, string data)
{
using (var client = new HttpClient())
{
var uri = new Uri($"http://{conn.Host}:{conn.Port}/datastores/{conn.Datastore}/content?operation=delete-content");
var content = new StringContent(data, Encoding.UTF8, "application/trig");

var request = new HttpRequestMessage(HttpMethod.Patch, uri)
{
Content = content
};


var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
}
}

/// <summary>
/// curl -i -X POST localhost:12110/datastores/boundaries/content -H "Content-Type: application/trig" -T boundaries.dlog
/// </summary>
/// <param name="conn"></param>
/// <param name="datalog"></param>
public static async Task LoadData(ConnectionSettings conn, string data)
{
using (var client = new HttpClient())
{
var uri = new Uri($"http://{conn.Host}:{conn.Port}/datastores/{conn.Datastore}/content");
var content = new StringContent(data, Encoding.UTF8, "application/trig");

var request = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = content
};


var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
}
}

/// <summary>
/// curl -i -X POST localhost:12110/datastores/boundaries/sparql -H "Accept: application/x.sparql-results+turtle-abbrev" -d "query=SELECT ?S ?P ?O WHERE { ?S ?P ?O }"
/// </summary>
/// <param name="conn"></param>
/// <param name="query"></param>
/// <returns></returns>
public static async Task<string> QuerySparql(ConnectionSettings conn, string query)
{
using (var client = new HttpClient())
{
var uri = new Uri($"http://{conn.Host}:{conn.Port}/datastores/{conn.Datastore}/sparql");
var content = new StringContent(query, Encoding.UTF8, "application/sparql-query");

var request = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = content
};

var response = await client.SendAsync(request);
if(!response.IsSuccessStatusCode)
throw new Exception(await response.Content.ReadAsStringAsync());
return await response.Content.ReadAsStringAsync();
}
}

}
11 changes: 11 additions & 0 deletions client/Boundaries/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Boundaries to Commissioning app

## Usage

Start a rdfox server from the folder [../../rdfox/](../../rdfox) with the following command:

```
RDFox sandbox ../../rdfox boundaries
```

Then run `dotnet run` and follow instructions
34 changes: 34 additions & 0 deletions client/Boundaries/TestBoundaries/DatalogCreatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace TestBoundaries;

public class DatalogCreatorTests
{
[Fact]
public void TestCreateDatalog()
{
var datalogCreator = new Boundaries.DatalogCreator();
var graphIri = datalogCreator.BoundaryGraph;
var datalog = datalogCreator.CreateBoundaryDatalogRule("T4750", new IriTools.IriReference[]
{
new IriTools.IriReference("https://assetid.equinor.com/plantx#Nozzle-12"),
new IriTools.IriReference("https://assetid.equinor.com/plantx#Nozzle-8"),
new IriTools.IriReference("https://assetid.equinor.com/plantx#PlateHeatExchanger-1"),
new IriTools.IriReference("https://assetid.equinor.com/plantx#ReciprocatingPump-1")
});
Assert.Equal( $$"""
<{{graphIri}}> [?node] :-
rdfs:label [?internal, "T4750"],
imf:connectedTo [?internal, ?node],
dexpi:PipingOrEquipment [?node].
<{{graphIri}}> [?node] :-
<{{graphIri}}> [?node1],
imf:connectedTo [?node1, ?node],
NOT FILTER(?node1 = <https://assetid.equinor.com/plantx#Nozzle-12>),
NOT FILTER(?node1 = <https://assetid.equinor.com/plantx#Nozzle-8>),
NOT FILTER(?node1 = <https://assetid.equinor.com/plantx#PlateHeatExchanger-1>),
NOT FILTER(?node1 = <https://assetid.equinor.com/plantx#ReciprocatingPump-1>),
dexpi:PipingOrEquipment [?node].
""" , datalog);
}
}
1 change: 1 addition & 0 deletions client/Boundaries/TestBoundaries/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
29 changes: 29 additions & 0 deletions client/Boundaries/TestBoundaries/TestBoundaries.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Boundaries\Boundaries.csproj" />
</ItemGroup>

</Project>
2 changes: 0 additions & 2 deletions datalog/boundary.datalog
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
prefix dexpi: <https://rdf.equinor.com/dexpi#>
prefix imf: <http://ns.imfid.org/imf#>
prefix data: <https://assetid.equinor.com/plantx/document/12345#>


Expand Down
Loading

0 comments on commit 0a1905a

Please sign in to comment.