Create Opc Ua Serveur c# with custom structure|OPC UA Implementation: Stacks, Tools, and Samples|Forum|OPC Foundation

Avatar
Search
Forum Scope


Match



Forum Options



Minimum search word length is 3 characters - maximum search word length is 84 characters
Lost password?
sp_Feed sp_PrintTopic sp_TopicIcon
Create Opc Ua Serveur c# with custom structure
Avatar
julien MARTIN
Member
Members
Forum Posts: 6
Member Since:
03/06/2024
sp_UserOfflineSmall Offline
1
03/06/2024 - 00:29
sp_Permalink sp_Print sp_EditHistory

Hello,

I want to create a c# OpcUa server to simulate the presence of a Siemens 1500 PLC Opc Ua server to test our application. I’ve managed to create the server with simple directories and variables, but I can’t manage to create an ExtensionObject variable with a given structure. I’d like to do it directly in the code and not go through xml and OpcUa Compiler.

This is how I created my slot object:

public class Slot : IEncodeable
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int[] Location { get; set; }
public int[] Rotation { get; set; }
public int[] Dimension { get; set; }

public Slot()
{
Location = new int[3] { 0, 0, 0 };
Rotation = new int[3] { 0, 0, 0 };
Dimension = new int[3] { 0, 0, 0 };
}

// Ajoutez ici les implémentations de Clone(), Encode() et Decode()…
public bool IsEqual(IEncodeable encodeable)
{
if (encodeable == null || !(encodeable is Slot))
{
return false;
}

Slot other = (Slot)encodeable;

if (ProductId != other.ProductId)
return false;
if (ProductName != other.ProductName)
return false;
if (!CompareArrays(Location, other.Location))
return false;
if (!CompareArrays(Rotation, other.Rotation))
return false;
if (!CompareArrays(Dimension, other.Dimension))
return false;

return true;
}

public object Clone()
{
// Créez une copie superficielle avec MemberwiseClone()
return this.MemberwiseClone();
}

private bool CompareArrays(T[] array1, T[] array2)
{
if (array1 == null && array2 == null)
return true;
if (array1 == null || array2 == null)
return false;
if (array1.Length != array2.Length)
return false;

for (int i = 0; i < array1.Length; i++)
{
if (!Equals(array1[i], array2[i]))
return false;
}
return true;
}
public void Encode(IEncoder encoder)
{
encoder.WriteInt32(“ProductId”, ProductId);
encoder.WriteString(“ProductName”, ProductName);
encoder.WriteInt32Array(“Location”, Location);
encoder.WriteInt32Array(“Rotation”, Rotation);
encoder.WriteInt32Array(“Dimension”, Dimension);
}

public void Decode(IDecoder decoder)
{
ProductId = decoder.ReadInt32(“ProductId”);
ProductName = decoder.ReadString(“ProductName”);
Location = decoder.ReadInt32Array(“Location”).ToArray();
Rotation = decoder.ReadInt32Array(“Rotation”).ToArray();
Dimension = decoder.ReadInt32Array(“Dimension”).ToArray();
}

public ExpandedNodeId TypeId => new ExpandedNodeId(“ns=2;n=SlotDataType”, 2);
public ExpandedNodeId BinaryEncodingId => new ExpandedNodeId(“ns=2;n=SlotDataType.BinaryEncoding”, 2);
public ExpandedNodeId XmlEncodingId => new ExpandedNodeId(“ns=2;n=SlotDataType.XmlEncoding”, 2);

}

Write the type in CustomNodeManager2 this way:

protected void CreateSlotDataTypeState()
{
var dataTypeId = new NodeId(“SlotDataType”, NamespaceIndex);
var binaryEncodingId = new NodeId(“SlotDataType.BinaryEncoding”, NamespaceIndex);

// Créer les champs pour le type de données Slot
List fields = new List
{
new StructureField
{
Name = “ProductId”,
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.Scalar,
IsOptional = false
},
new StructureField
{
Name = “ProductName”,
DataType = DataTypeIds.String,
ValueRank = ValueRanks.Scalar,
IsOptional = false
},
new StructureField
{
Name = “Location”,
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.OneDimension,
ArrayDimensions = new uint[] { 3 },
IsOptional = false
},
new StructureField
{
Name = “Rotation”,
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.OneDimension,
ArrayDimensions = new uint[] { 3 },
IsOptional = false
},
new StructureField
{
Name = “Dimension”,
DataType = DataTypeIds.Int32,
ValueRank = ValueRanks.OneDimension,
ArrayDimensions = new uint[] { 3 },
IsOptional = false
}
};

// Créer la définition de la structure pour Slot
StructureDefinition structureDefinition = new StructureDefinition
{
BaseDataType = DataTypeIds.Structure,
StructureType = StructureType.Structure,
Fields = fields.ToArray()
};

// Enregistrer le type de données Slot
var slotDataType = new DataTypeState
{
NodeId = dataTypeId,
BrowseName = new QualifiedName(“SlotDataType”, NamespaceIndex),
DisplayName = “Slot”,
Description = “A custom Slot data type.”,
IsAbstract = false,
WriteMask = AttributeWriteMask.None,
UserWriteMask = AttributeWriteMask.None,
DataTypeDefinition = new ExtensionObject(structureDefinition)

};

var binaryEncoding = new BaseObjectState(slotDataType)
{
NodeId = binaryEncodingId,
BrowseName = new QualifiedName(“BinaryEncoding”, NamespaceIndex),
DisplayName = new LocalizedText(“Binary Encoding”),
// Autres propriétés
};

// Lier l’encodage binaire au type de données
slotDataType.AddReference(ReferenceTypes.HasEncoding, false, binaryEncoding.NodeId);
binaryEncoding.AddReference(ReferenceTypes.HasEncoding, true, slotDataType.NodeId);

// Enregistrement du type Slot.
// EncodeableFactory.GlobalFactory.AddEncodeableType(typeof(Slot));
AddPredefinedNode(SystemContext, binaryEncoding);
// Ajoutez le DataTypeNode à l’espace d’adressage du serveur
AddPredefinedNode(SystemContext, slotDataType);
}

and add my variable:

// Production Unit
FolderState pus = CreateFolder(null, “Pus”);
pus.AddReference(ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder);
references.Add(new NodeStateReference(ReferenceTypes.Organizes, false, pus.NodeId));
pus.EventNotifier = EventNotifiers.SubscribeToEvents;
AddRootNotifier(pus);

// Create a variable node using the Slot data type
for (int i = 0; i < 2; i++)
{
FolderState currentPu = CreateFolder(pus, $”Pu_{i}”);
currentPu.AddReference(ReferenceTypes.Organizes, true, ObjectIds.ObjectsFolder);
currentPu.EventNotifier = EventNotifiers.SubscribeToEvents;

NodeId slotDataTypeId = new NodeId(“SlotDataType”, NamespaceIndex);

Slot slot = new Slot
{
ProductId = 1,
ProductName = “Product A”,
Location = new int[3] { 0, 0, 0 },
Rotation = new int[3] { 0, 0, 0 },
Dimension = new int[3] { 0, 0, 0 }
};

ExtensionObject extensionObject = new ExtensionObject(slot.BinaryEncodingId,slot);

// Créez ici la variable en utilisant le type de données ‘Slot’.
var slotVariable = new BaseDataVariableState(currentPu)
{
NodeId = new NodeId($”SlotVariable_{i}”, NamespaceIndex),
BrowseName = new QualifiedName($”SlotVariable_{i}”, NamespaceIndex),
DisplayName = $”Slot Variable {i}”,
TypeDefinitionId = VariableTypeIds.BaseDataVariableType,
DataType = slotDataTypeId, // Utilisez l’NodeId du type de données ‘Slot’.
ValueRank = ValueRanks.Scalar,
AccessLevel = AccessLevels.CurrentReadOrWrite,
UserAccessLevel = AccessLevels.CurrentReadOrWrite,
Historizing = false,
Value = extensionObject

};
// Ajoutez la variable au modèle d’adresse
currentPu.AddChild(slotVariable);
}
AddPredefinedNode(SystemContext, pus);

The object is created, but the OpcUa client can’t decode the Byte[] object.

Thank you in advance for your help.

Best regards.

Avatar
Marco Olmi
New Member
Members
Forum Posts: 1
Member Since:
12/07/2023
sp_UserOfflineSmall Offline
2
09/03/2024 - 11:44
sp_Permalink sp_Print

Hallo, facing same problems…. have you had success?

Avatar
julien MARTIN
Member
Members
Forum Posts: 6
Member Since:
03/06/2024
sp_UserOfflineSmall Offline
3
09/04/2024 - 07:47
sp_Permalink sp_Print

Hello.

Yes , I Have create my structure in free software UAModeler and export xml file.

Build  xml with Opc.Ua.ModelCompiler.exe.

I Import résulte build in my c# solution. I Have create my object with previous build class.

Best regard.

Forum Timezone: America/Phoenix
Most Users Ever Online: 510
Currently Online:
Guest(s) 31
Currently Browsing this Page:
1 Guest(s)
Top Posters:
Forum Stats:
Groups: 2
Forums: 10
Topics: 1446
Posts: 4890