Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not add custom types into global EncodeableFactory #371

Merged
merged 6 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/PlcServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ namespace OpcPlc;
using System.IO;
using System.Reflection;
using System.Threading;

using Meters = OpcPlc.MetricsConfig;

public partial class PlcServer : StandardServer
Expand Down Expand Up @@ -260,11 +259,18 @@ public override ResponseHeader Write(RequestHeader requestHeader, WriteValueColl
/// </remarks>
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
{
var nodeManagers = new List<INodeManager>();
var nodeManagers = new List<INodeManager>();

// When used via NuGet package in-memory, the server needs to use it's own encodeable factory.
// Otherwise the client will not load the type definitions for decoding correctly. There is currently no public
// API to set the encodeable factory and it is not possible to provide own implementation, because other classes
// require the StandardServer or ServerInternalData as objects, so we need to use reflection to set it.
var serverInternalDataField = typeof(StandardServer).GetField("m_serverInternal", BindingFlags.Instance | BindingFlags.NonPublic);
var encodeableFactoryField = serverInternalDataField.FieldType.GetField("m_factory", BindingFlags.Instance | BindingFlags.NonPublic);
encodeableFactoryField.SetValue(server, new EncodeableFactory(false));

// Add encodable complex types.
server.Factory.AddEncodeableTypes(Assembly.GetExecutingAssembly());
EncodeableFactory.GlobalFactory.AddEncodeableTypes(Assembly.GetExecutingAssembly());

// Add DI node manager first so that it gets the namespace index 2.
var diNodeManager = new DiNodeManager(server, configuration);
Expand Down Expand Up @@ -423,9 +429,9 @@ protected override void OnServerStopping()
IList<Session> currentSessions = ServerInternal.SessionManager.GetSessions();

if (currentSessions.Count > 0)
{
// provide some time for the connected clients to detect the shutdown state.
ServerInternal.Status.Value.ShutdownReason = new LocalizedText(string.Empty, "Application closed."); // Invariant.
{
// provide some time for the connected clients to detect the shutdown state.
ServerInternal.Status.Value.ShutdownReason = new LocalizedText(string.Empty, "Application closed."); // Invariant.
ServerInternal.Status.Variable.ShutdownReason.Value = new LocalizedText(string.Empty, "Application closed."); // Invariant.
ServerInternal.Status.Value.State = ServerState.Shutdown;
ServerInternal.Status.Variable.State.Value = ServerState.Shutdown;
Expand Down
8 changes: 7 additions & 1 deletion src/PluginNodes/ComplexTypeBoilerPluginNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ 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.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.
// TODO: Find a better solution to avoid this dependency between boilers.
Expand Down
28 changes: 23 additions & 5 deletions tests/BoilerTests.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
namespace OpcPlc.Tests;
namespace OpcPlc.Tests;

using BoilerModel1;
using FluentAssertions;
using NUnit.Framework;
using Opc.Ua;
using Opc.Ua.Client.ComplexTypes;
using System.Dynamic;
using System.Text.Json;
using static System.TimeSpan;

/// <summary>
/// Tests for the Boiler, which is a complex type.
/// </summary>
[TestFixture]
public class BoilerTests : SimulatorTestsBase
{
{
private ComplexTypeSystem _complexTypeSystem;
public BoilerTests() : base(new[] { "--ctb" })
{
}

[OneTimeSetUp]
public void OneTimeSetUp()
{
_complexTypeSystem = new ComplexTypeSystem(Session);
var loaded = _complexTypeSystem.LoadNamespace(OpcPlc.Namespaces.OpcPlcBoiler).ConfigureAwait(false).GetAwaiter().GetResult();
loaded.Should().BeTrue("BoilerDataType should be loaded");
}

[TearDown]
public new virtual void TearDown()
Expand Down Expand Up @@ -120,7 +132,13 @@ private void TurnHeaterOff()
private BoilerDataType GetBoilerModel()
{
var nodeId = NodeId.Create(BoilerModel1.Variables.Boiler1_BoilerStatus, OpcPlc.Namespaces.OpcPlcBoiler, Session.NamespaceUris);
var value = Session.ReadValue(nodeId).Value;
return value.Should().BeOfType<ExtensionObject>().Which.Body.Should().BeOfType<BoilerDataType>().Subject;
var value = Session.ReadValue(nodeId).Value;

// change dynamic in-memory created Boiler type to expected BoilerDataType by serializing and deserializing it.
var inmemoryBoilerDataType = (value as ExtensionObject).Body;
var json = JsonSerializer.Serialize(inmemoryBoilerDataType);

var boilerDataTypeFromGeneratedSourceCode = JsonSerializer.Deserialize<BoilerDataType>(json);
return boilerDataTypeFromGeneratedSourceCode;
}
}
6 changes: 3 additions & 3 deletions tests/DeterministicAlarmsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace OpcPlc.Tests;
using NUnit.Framework;
using Opc.Ua;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using static System.TimeSpan;

[TestFixture]
Expand All @@ -26,14 +26,14 @@ public DeterministicAlarmsTests() : base(new[]
[SetUp]
public void CreateMonitoredItem()
{
SetUpMonitoredItem(Objects.Server, NodeClass.Object, Attributes.EventNotifier);
SetUpMonitoredItem(AlarmNodeId("VendingMachines"), NodeClass.Object, Attributes.EventNotifier);

AddMonitoredItem();
}

[Test]
public void FiresEventSequence()
{
{
var machine1 = AlarmNodeId("VendingMachine1");
var machine2 = AlarmNodeId("VendingMachine2");

Expand Down
2 changes: 1 addition & 1 deletion tests/DeterministicAlarmsTests2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public DeterministicAlarmsTests2() : base(new[]
[SetUp]
public void CreateMonitoredItem()
{
SetUpMonitoredItem(Objects.Server, NodeClass.Object, Attributes.EventNotifier);
SetUpMonitoredItem(AlarmNodeId("VendingMachines"), NodeClass.Object, Attributes.EventNotifier);

AddMonitoredItem();
}
Expand Down
Loading