Skip to content

Commit

Permalink
Snap with dotnet plugin (#392)
Browse files Browse the repository at this point in the history
* chore: Add snapcraft.yaml and run script

* fix: override appsettings.json location to $SNAP

* fix: override uanodes file locations to $SNAP

* chore: Add snap hooks and update run script

---------

Co-authored-by: Mateus Rodrigues de Morais <[email protected]>
  • Loading branch information
s-themis and mateusrodrigues authored Sep 11, 2024
1 parent a6b3b0e commit 1ca5812
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 11 deletions.
16 changes: 16 additions & 0 deletions scripts/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh -e

trustedcertbase64="$(snapctl get trustedcertbase64)"

cmd="\"$SNAP\"/opcplc --pn=50000 --sn=10 --sr=10 --st=uint --fn=10 --fr=1 --ft=uint --gn=10 \
--appcertstorepath=\"$SNAP_USER_DATA/pki/own\" \
--trustedcertstorepath=\"$SNAP_USER_DATA/pki/trusted\" \
--rejectedcertstorepath=\"$SNAP_USER_DATA/pki/rejected\" \
--issuercertstorepath=\"$SNAP_USER_DATA/pki/issuer\" \
--logfile=\"$SNAP_USER_DATA/hostname-port-plc.log\""

if [ -n "$trustedcertbase64" ]; then
cmd="$cmd --addtrustedcertfile=\"$SNAP_DATA/config/pki/trusted/certs/cert_1.crt\""
fi

eval "$cmd"
15 changes: 15 additions & 0 deletions snap/hooks/configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/sh -e

# Supported keys:
# - trustedcertbase64 (string)
# Certificate in base64 string format to be trusted by the server.

handle_trustedcertbase64()
{
trustedcertbase64="$(snapctl get trustedcertbase64)"
if [ -n "$trustedcertbase64" ]; then
echo "$trustedcertbase64" > "$SNAP_DATA/config/pki/trusted/certs/cert_1.crt"
fi
}

handle_trustedcertbase64
3 changes: 3 additions & 0 deletions snap/hooks/default-configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh -e

mkdir -p "$SNAP_DATA/config/pki/trusted/certs"
36 changes: 36 additions & 0 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: iot-edge-opc-plc
base: core22
version: '0.1'
summary: Sample OPC UA server
description: |
Sample OPC UA server with nodes that generate random
and increasing data, anomalies and much more.
grade: stable
confinement: strict # use 'strict' once you have the right plugs and slots

parts:
opc-plc:
plugin: dotnet
dotnet-build-configuration: Release
dotnet-self-contained-runtime-identifier: linux-x64
source: .
build-packages:
- dotnet-sdk-8.0
scripts:
plugin: dump
source: scripts/
organize:
'*' : scripts/
appsettings:
plugin: dump
source: src/
prime:
- appsettings.json

apps:
opc-plc:
command: scripts/run
plugs:
- network
- network-bind
11 changes: 10 additions & 1 deletion src/CompanionSpecs/DI/DiNodeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

using Opc.Ua;
using Opc.Ua.Server;
using System;
using System.IO;
using System.Reflection;

/// <summary>
Expand Down Expand Up @@ -29,9 +31,16 @@ public DiNodeManager(IServerInternal server, ApplicationConfiguration _)
/// </summary>
protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
var uanodesPath = "CompanionSpecs/DI/Opc.Ua.DI.PredefinedNodes.uanodes";
var snapLocation = Environment.GetEnvironmentVariable("SNAP");
if (!string.IsNullOrWhiteSpace(snapLocation))
{
// Aplication running as a snap
uanodesPath = Path.Join(snapLocation, uanodesPath);
}
var predefinedNodes = new NodeStateCollection();
predefinedNodes.LoadFromBinaryResource(context,
"CompanionSpecs/DI/Opc.Ua.DI.PredefinedNodes.uanodes", // CopyToOutputDirectory -> PreserveNewest.
uanodesPath, // CopyToOutputDirectory -> PreserveNewest.
typeof(DiNodeManager).GetTypeInfo().Assembly,
updateTables: true);

Expand Down
10 changes: 9 additions & 1 deletion src/OpcPlcServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,17 @@ private void InitLogging()
/// </summary>
public IHost CreateHostBuilder(string[] args)
{
var contentRoot = Directory.GetCurrentDirectory();
var snapLocation = Environment.GetEnvironmentVariable("SNAP");
if (!string.IsNullOrWhiteSpace(snapLocation))
{
// The application is running as a snap
contentRoot = snapLocation;
}

var host = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => {
webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); // Avoid System.InvalidOperationException.
webBuilder.UseContentRoot(contentRoot); // Avoid System.InvalidOperationException.
webBuilder.UseUrls($"http://*:{Config.WebServerPort}");
webBuilder.UseStartup<Startup>();
}).Build();
Expand Down
13 changes: 11 additions & 2 deletions src/PluginNodes/Boiler2PluginNodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace OpcPlc.PluginNodes;
using OpcPlc.PluginNodes.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
Expand Down Expand Up @@ -41,7 +42,7 @@ public class Boiler2PluginNodes(TimeService timeService, ILogger logger) : Plugi
private TimeSpan _overheatInterval = TimeSpan.FromSeconds(120); // 2 min.

private bool _isOverheated;
private readonly SemaphoreSlim _lock = new(1, 1);
private readonly SemaphoreSlim _lock = new(1, 1);

public void AddOptions(Mono.Options.OptionSet optionSet)
{
Expand Down Expand Up @@ -154,10 +155,18 @@ private void AddNodes()
/// </summary>
private NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
var uanodesPath = "Boilers/Boiler2/BoilerModel2.PredefinedNodes.uanodes";
var snapLocation = Environment.GetEnvironmentVariable("SNAP");
if (!string.IsNullOrWhiteSpace(snapLocation))
{
// Aplication running as a snap
uanodesPath = Path.Join(snapLocation, uanodesPath);
}

var predefinedNodes = new NodeStateCollection();

predefinedNodes.LoadFromBinaryResource(context,
"Boilers/Boiler2/BoilerModel2.PredefinedNodes.uanodes", // CopyToOutputDirectory -> PreserveNewest.
uanodesPath, // CopyToOutputDirectory -> PreserveNewest.
typeof(PlcNodeManager).GetTypeInfo().Assembly,
updateTables: true);

Expand Down
21 changes: 15 additions & 6 deletions src/PluginNodes/ComplexTypeBoilerPluginNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace OpcPlc.PluginNodes;
using OpcPlc.PluginNodes.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Timers;

Expand All @@ -17,7 +18,7 @@ public class ComplexTypeBoilerPluginNode(TimeService timeService, ILogger logger
{
private PlcNodeManager _plcNodeManager;
private Boiler1State _node;
private ITimer _nodeGenerator;
private ITimer _nodeGenerator;

public void AddOptions(Mono.Options.OptionSet optionSet)
{
Expand Down Expand Up @@ -58,12 +59,12 @@ private void AddNodes(FolderState methodsFolder)

// Convert to node that can be manipulated within the server.
_node = new Boiler1State(null);
_node.Create(_plcNodeManager.SystemContext, passiveBoiler1Node);
_node.BoilerStatus.Value = new BoilerDataType {
Pressure = 99_000,
_node.Create(_plcNodeManager.SystemContext, passiveBoiler1Node);
_node.BoilerStatus.Value = new BoilerDataType {
Pressure = 99_000,
Temperature = new BoilerTemperatureType { Bottom = 100, Top = 95 },
HeaterState = BoilerHeaterStateType.On,
};
};
_node.BoilerStatus.ClearChangeMasks(_plcNodeManager.SystemContext, includeChildren: true);

// Put Boiler #2 into Boilers folder.
Expand All @@ -90,10 +91,18 @@ private void AddNodes(FolderState methodsFolder)
/// </summary>
private NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
var uanodesPath = "Boilers/Boiler1/BoilerModel1.PredefinedNodes.uanodes";
var snapLocation = Environment.GetEnvironmentVariable("SNAP");
if (!string.IsNullOrWhiteSpace(snapLocation))
{
// Aplication running as a snap
uanodesPath = Path.Join(snapLocation, uanodesPath);
}

var predefinedNodes = new NodeStateCollection();

predefinedNodes.LoadFromBinaryResource(context,
"Boilers/Boiler1/BoilerModel1.PredefinedNodes.uanodes", // CopyToOutputDirectory -> PreserveNewest.
uanodesPath, // CopyToOutputDirectory -> PreserveNewest.
typeof(PlcNodeManager).GetTypeInfo().Assembly,
updateTables: true);

Expand Down
11 changes: 10 additions & 1 deletion src/SimpleEvent/SimpleEventsNodeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace SimpleEvents;
using OpcPlc.SimpleEvent;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;

Expand Down Expand Up @@ -88,9 +89,17 @@ public override NodeId New(ISystemContext context, NodeState node)
/// </summary>
protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
{
var uanodesPath = "SimpleEvent/SimpleEvents.PredefinedNodes.uanodes";
var snapLocation = Environment.GetEnvironmentVariable("SNAP");
if (!string.IsNullOrWhiteSpace(snapLocation))
{
// Aplication running as a snap
uanodesPath = Path.Join(snapLocation, uanodesPath);
}

var predefinedNodes = new NodeStateCollection();
predefinedNodes.LoadFromBinaryResource(context,
"SimpleEvent/SimpleEvents.PredefinedNodes.uanodes",
uanodesPath,
typeof(SimpleEventsNodeManager).GetTypeInfo().Assembly,
updateTables: true);
return predefinedNodes;
Expand Down

0 comments on commit 1ca5812

Please sign in to comment.